본문 바로가기

Swift

Swift - UIPageViewController

스위프트에서 UIPageViewController를 사용하면 여러 ViewController들을 하나의 PageViewController로 보여 줄 수 있습니다.

간단하게 책장을 넘기듯이 스와이프를 하면서 다음, 이전 뷰컨트롤러로 쉽게 이동이 가능합니다.

 

먼저 세개의 뷰컨트롤러를 만들고 First, Second, Thrid 라고 스토리보드 ID를 설정해줬습니다.

 

먼저 pageViewControllers배열안에 PageViewController에서 사용할 ViewController들을 담아줍니다.

newViewContoller함수는 String을 받아 ViewController의 StoryboadID를 찾아서 UIViewController로 쉽게 변환해주는 함수입니다.

 

그리고 viewDidLoad()에서 PageViewController의 첫번째 화면을 설정해줍니다.

 

class ViewController: UIPageViewController {
    
    lazy var pageViewControllers: [UIViewController] = {
        return [newViewControllers(named: "First"),
        newViewControllers(named: "Second"),
        newViewControllers(named: "Third")]
    }()
    
    func newViewControllers(named: String) -> UIViewController {
        return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: named)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        dataSource = self
        
        if let firstViewController = pageViewControllers.first {
            setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
        }
    }
}

 

 

UIPageViewControllerDataSource를 채택한 후에 viewControllerBefore, viewControllerAfter에서 이전 다음 화면에 대한 세팅을 해줍니다.

Before인 경우 viewController들의 배열에서 -1 을 해서 이전 뷰컨트롤러를 불러오는데 만약 0번째이면 더이상 보여지지않고

After에선 반대로 +1을 해서 새로운 ViewController를 불러오다 마지막인 경우에는 불러오지 않습니다.

extension ViewController: UIPageViewControllerDataSource {
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return pageViewControllers.count
    }
    
    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        guard let firstViewController = viewControllers?.first,
              let firstViewControllerIndex = pageViewControllers.firstIndex(of: firstViewController) else {
            return 0
        }
        
        return firstViewControllerIndex
    }
    
    //이전 화면
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = pageViewControllers.firstIndex(of: viewController) else { return nil }
        
        let previousIndex = viewControllerIndex - 1
        let pageViewControllersCount = pageViewControllers.count
        
        guard previousIndex >= 0 else { return nil }
        
        guard pageViewControllersCount > previousIndex else { return nil }
        
        return pageViewControllers[previousIndex]
    }
    
    //다음 화면
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = pageViewControllers.firstIndex(of: viewController) else { return nil }
        
        let nextIndex = viewControllerIndex + 1
        let pageViewControllersCount = pageViewControllers.count
        
        guard pageViewControllersCount != nextIndex else { return nil }
        
        guard pageViewControllersCount > nextIndex else { return nil }
        
        return pageViewControllers[nextIndex]
    }
    
}

 

presentationCount, presentationIndex 두 함수는 pageControl을 보여줄 때 이용됩니다.

 

StoryBoard에서 PageViewController의 Transition Style을 설정가능한데 Page Curl을 선택한 경우 책장을 넘기는 애니메이션이 자동으로 적용됩니다.

Scroll을 선택하게 되면 애니메이션은 없어지고 단순히 스크롤만 되지만 pageControl이 아래쪽에 자동으로 만들어집니다.

이때 presentationCount, presentationIndex의 설정에 따라 pageControl이 보여지게 됩니다.

 

 

추가적으로

만약 무한스크롤 마지막화면에서 다시 한번더 스와이프하면 첫번째로 가고 첫번째에서 한번 더 스와이프하면 마지막으로 가도록 하고 싶으면 viewControllerBefore, viewControllerAfter에서

아래처럼 수정해주면 무한 스크롤로 변경됩니다.

//기존
guard previousIndex >= 0 else { return nil }
//수정
guard previousIndex >= 0 else { return pageViewControllers.last }

//기존
guard pageViewControllersCount != nextIndex else { return nil }
//수정
guard pageViewControllersCount != nextIndex else { return pageViewControllers.first }

 

이렇게 만들게 되면 문제없이 동작하지만 아래쪽 pageControl이 들어가는 부분이 검정색으로 보인다

이유는 뷰 계층을 보게 되면 viewController들은 위쪽에 보여지고 아래 빈공간이 생기고 그 안에 조그맣게 pageControl이 들어가다보니 pageControl을 뺀 나머지 공간들은 검정색으로 아무것도 없는 것으로 처리가 된다.

 

 

이럴때는 view의 subViews중에 viewController들이 올라가는 UIScrollView의 사이즈를 스크린만큼 키운다음 pageControl의 배경을 투명하게 해주면 꽉찬 화면으로 나타나게 됩니다.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    view.subviews.forEach {
        if $0 is UIScrollView {
            $0.frame = UIScreen.main.bounds
        } else if $0 is UIPageControl {
            $0.backgroundColor = UIColor.clear
        }
    }
}

'Swift' 카테고리의 다른 글

Error Finding App Store Connect Credentials  (0) 2023.11.10
Swift - 그라데이션  (0) 2021.02.23
Swift - zip 함수  (0) 2021.02.08
Swift 제곱근  (0) 2021.01.18
에라토스테네스의 체와 Stride()  (0) 2021.01.15