Search code examples
iosswiftuipageviewcontroller

Crash in UIPageViewController when adding pages


In the iOS 11 app I'm developing (using Swift 4), I need to dynamically add pages to a UIPageViewController. However, in some circumstances, I receive the following crash error (NSInternalInconsistencyException):

2017-11-27 14:55:54.787260+0000 MyApp[380:79434] *** Assertion failure in -[UIPageViewController _flushViewController:animated:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3698.21.8/UIPageViewController.m:2124
2017-11-27 14:55:54.791022+0000 MyApp[380:79434] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Don't know about flushed view <UIView: 0x12dd48ac0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x1c003c280>>'

I am very new to iOS development, so I'm sorry if the answer is obvious, but I'm having a hard time finding the root cause for the crash. The logged message isn't being very helpful either...

This is the code I'm using to add pages:

//Inflate the view controller
let viewController = newViewController(param: someParam )

//Add it to the view controllers pool
viewControllersOrdered.insert(viewController, at: getInsertionIndex(param: someOtherParam) )

//Add it to the pageViewController
pageViewController.setViewControllers([viewController], direction: .forward, animated: false, completion: nil)

//Force cache to be cleared
pageViewController.dataSource = nil;
pageViewController.dataSource = self;

The getInsertionIndex function is working correctly, that is not the source of the crash.


Solution

  • In the iOS 11 app I'm developing (using Swift 4), I need to dynamically add pages to a UIPageViewController.

    It sounds like what you mean is: you do not know what the next / previous view controller will be until the user actually tries to "turn the page". In that case, a scrolling UIPageViewController is not a good fit for your architecture (because, as you have already figured out, it precaches the next and previous pages). You have two choices:

    • Use a page-curl UIPageViewController. It doesn't precache its pages, so you are not called upon to make a decision until the user actually asks to turn the page.

    • Don't use a UIPageViewController at all; use a paging horizontal scroll view and manage the content view yourself.

    Also, if you're using a UIPageViewController, in no case should you also be holding a retained list of child view controllers. That is the job of the UIPageViewController. Your job is merely to supply the next or previous view controller, which you should create on demand and at no other time, and release it into the UIPageViewController's sole control.