なんとかするから、なんとかなる

エンジニア関係のことを書きます

iOS UIPageViewControllerで"Unbalanced calls to begin/end appearance transitions for" が出たときの対処方法

English version below

はじめに

UIPageViewControllerを早送りしたときに次のようなエラーが出ました。

Unbalanced calls to begin/end appearance transitions for 

そのときのコードはこんな感じです。

class MyPageViewController: UIPageViewController {
    var maxPage = 3
    var currentPage = 0

    ~~~~
    // SomeCode....
    ~~~~
}

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard self.currentPage > self.maxPage else { return nil }
        self.currentPage += 1
        
        let myAfterVC = MyAfterViewController()
        
        return myAfterVC
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard self.currentPage >=  0 else { return nil }
        self.currentPage -= 1
        
        let myBeforeVC = MyBeforeViewController()
        
        return myBeforeVC
    }
}

原因

このエラーメッセージは、現在のUIViewControllerの表示が完了する前に、次のUIViewControllerへ遷移しようとしたときに出るようです。

解決方法

遷移中に再度遷移しないようにフラグをもたせました。

UIPageViewControllerDelegateを通して、で遷移の開始と終了を知ることができます。

したがって、コードでは次のようになります。

class MyPageViewController: UIPageViewController {
    var isTransitioning = false
    var maxPage = 3
    var currentPage = 0

    ~~~~
    // SomeCode....
    ~~~~
}

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard self.currentPage < self.maxPage, self.isTransitioning == false else { return nil }
        self.currentPage += 1
        
        let myAfterVC = MyAfterViewController()
        
        return myAfterVC
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard self.currentPage >=  0, self.isTransitioning == false else { return nil }
        self.currentPage -= 1
        
        let myBeforeVC = MyBeforeViewController()
        
        return myBeforeVC
    }
}

extension MyPageViewController: UIPageViewControllerDelegate {
    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
        self.isTransitioning = true
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        self.isTransitioning = false
    }
}