Assertion failure in UIPageViewController

后端 未结 6 581
醉梦人生
醉梦人生 2020-12-14 20:43

I have an Assertion Failure in UIPageViewController.

Assertion failure in -[UIPageViewController _flushViewController:animated:], /BuildRoot/Library/Caches/c         


        
相关标签:
6条回答
  • 2020-12-14 21:14

    The direct way to run into this assert is to use cycled source for UIPageController defined with scroll transition style.

    When the source contains two pages each one is the previous and the next for another one. If you swipe UIPageController containing two pages and then try to set source with 3 pages you will get the assertion mentioned above with guarantee assuming that UIPageControllerDataSource before/after methods allow cycled transition in case of 2 pages.

    The main rules of crash-free using UIPageController with scroll transition:

    1) set dataSource before calling setViewControllers method

    2) use setViewControllers method without animation (animated: false)

    3) set dataSource to nil for single page mode

    4) don't allow cycles for 2-page mode

    All these recommendations together make UIPageController absolutely stable.

    import UIKit
    
    /// Convenient subclass of UIPageViewController
    @objc class AMPageViewController: UIPageViewController {
    
        /// Turn on/off PageControl at the bottom
        @objc var showPageControl: Bool = true
    
        /// Array of all viewControllers
        @objc var source: [UIViewController]? {
    
            didSet {
                let count = source?.count ?? 0
                if count > 0 {
                    dataSource = count > 1 ? self : nil
                }
                else {
                    dataSource = nil
                    delegate = nil
                }
            }
        }
    
        /// Index of the current viewController from source
        @objc var pageIndex: Int {
    
            get {
                var currentPageIndex: Int = 0
                if let vc = viewControllers?.first, let source = source, let pageIndex = source.index(of: vc) {
                    currentPageIndex = pageIndex
                }
    
                return currentPageIndex
            }
    
            set {
                guard newValue >= 0, let source = source, newValue < source.count else { return }
    
                let vc = source[newValue]
                let direction: UIPageViewControllerNavigationDirection = newValue < pageIndex ? .reverse : .forward
    
                setViewController(vc, direction: direction)
            }
        }
    
        override weak var delegate: UIPageViewControllerDelegate? {
    
            get { return super.delegate }
    
            set {
                if source?.count ?? 0 > 0 {
                    super.delegate = newValue
                }
                else {
                    super.delegate = nil
                }
            }
        }
    
        /// Initializer in scroll-mode with interPageSpacing
        @objc init(navigationOrientation: UIPageViewControllerNavigationOrientation = .horizontal, interPageSpacing: Int = 0) {
    
            let options = (interPageSpacing > 0) ? [UIPageViewControllerOptionInterPageSpacingKey : 5] : nil
    
             super.init(transitionStyle: .scroll, navigationOrientation: navigationOrientation, options: options)
         }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        /// Set viewcontroller by index from source
        @objc func setPageIndex(_ index: Int, completion: ((Bool) -> Void)? = nil) {
    
            guard index > 0, let source = source, index < source.count else { return }
    
            let vc = source[index]
            let direction: UIPageViewControllerNavigationDirection = index < pageIndex ? .reverse : .forward
    
            setViewController(vc, direction: direction, completion: completion)
        }
    
    
        private func setViewController(_ viewController: UIViewController, direction: UIPageViewControllerNavigationDirection = .forward, completion: ((Bool) -> Void)? = nil) {
    
            super.setViewControllers([viewController], direction: direction, animated: false, completion: completion)
        }
    }
    
    extension FFPageViewController: UIPageViewControllerDataSource {
    
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
    
            guard let source = source, let index = source.index(of: viewController) else { return nil }
    
            let count = source.count
    
            if count == 2, index == 0 {
                return nil
            }
    
            let prevIndex = (index - 1) < 0 ? count - 1 : index - 1
    
            let pageContentViewController: UIViewController = source[prevIndex]
    
            return pageContentViewController
        }
    
    
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
    
            guard let source = source, let index = source.index(of: viewController) else { return nil }
    
            let count = source.count
    
            if count == 2, index == 1 {
                return nil
            }
    
            let nextIndex = (index + 1) >= count ? 0 : index + 1
    
            let pageContentViewController = source[nextIndex]
    
            return pageContentViewController
        }
    
    
        func presentationCount(for pageViewController: UIPageViewController) -> Int {
    
            return showPageControl ? (source?.count ?? 0) : 0
        }
    
    
        func presentationIndex(for pageViewController: UIPageViewController) -> Int {
    
            guard showPageControl else { return 0 }
    
            return pageIndex
        }
    }
    

    The overall implementation and usage examples one can find at GitHub project.

    0 讨论(0)
  • 2020-12-14 21:17

    For me, the issue was using self.pageViewController as a member of the current view controller instead of pageViewController as parameter obtained in the didFinishAnimating delegate method.

    0 讨论(0)
  • 2020-12-14 21:23

    Move your pageViewController.setViewControllers function call inside DispatchQueue.main.async block if you are doing it in code.

    I don't know why it works but it worked for me. For reference.

    0 讨论(0)
  • 2020-12-14 21:27

    When UIPageViewController transition, ViewController inside it(ex: UITableViewController) transition will cause crash.

    In my case (crash):

    step1

    self.pageViewController.setViewControllers([self.tableViewController2], direction: .forward, animated: true, completion: nil)
    

    step2

    Scroll the tableView while UIPageViewController transition.

    My Solution

    (disable scroll both target view controller and current view controller)

    self.tableViewController1.tableView.isScrollEnabled = false
    self.tableViewController2.tableView.isScrollEnabled = false
    self.pageViewController.setViewControllers([self.tableViewController2], direction: .forward, animated: true, completion: { _ in
        self.tableViewController1.tableView.isScrollEnabled = true
        self.tableViewController2.tableView.isScrollEnabled = true
    })
    
    0 讨论(0)
  • 2020-12-14 21:28

    This happened to me too when I had textfields in child controller and didn't dismiss keyboard on scroll to next controller. If this is case just add endEditing in action where you programmatically change your controller or if you are scrolling on scrollViewDidScroll delegate method of pageViewController

    0 讨论(0)
  • 2020-12-14 21:31

    This happens when your UIPageViewControllerTransitionStyle is set to scroll instead of pageCurl.

    Are you dynamically creating View Controllers and setting them on UIPageViewController? In that case, you must ensure that the second call to setViewControllers is called after the first one completes animation because of a bug in UIKit. A delayed dispatch is a quick and dirty fix, though it is not a good practice

    More details here.

    https://forums.developer.apple.com/thread/6554

    0 讨论(0)
提交回复
热议问题