Search code examples
iosswiftuipageviewcontroller

'NSInternalInconsistencyException', reason: 'Unexpected subviews' possibly in UIPageViewController (swift, iOS)


I've been stuck on this for the last couple of hours and cannot seem to find a solution anywhere. After swiping on the first view controller displayed by my UIPageViewController subclass, the app crashes with the exception 'NSInternalInconsistencyException', reason: 'Unexpected subviews'. I really don't understand what it means and how I should fix the error. The call stack is as follows:

0   CoreFoundation                      0x00000001123561ab __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x00000001119ebf41 objc_exception_throw + 48
2   CoreFoundation                      0x000000011235b372 +[NSException raise:format:arguments:] + 98
3   Foundation                          0x0000000111490089 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193
4   UIKit                               0x000000011565c2e1 -[_UIQueuingScrollView _setWrappedViewAtIndex:withView:] + 377
5   UIKit                               0x000000011565ca09 -[_UIQueuingScrollView _replaceViews:updatingContents:adjustContentInsets:animated:] + 734
6   UIKit                               0x000000011565d00f -[_UIQueuingScrollView _viewAtIndex:loadingIfNecessary:updatingContents:animated:] + 379
7   UIKit                               0x0000000115660664 __54-[_UIQueuingScrollView _didScrollWithAnimation:force:]_block_invoke + 109
8   UIKit                               0x00000001156602e8 -[_UIQueuingScrollView _didScrollWithAnimation:force:] + 534
9   UIKit                               0x000000011565b9f3 -[_UIQueuingScrollView layoutSubviews] + 179
10  UIKit                               0x0000000114c746f5 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1439
11  QuartzCore                          0x0000000117f483ee -[CALayer layoutSublayers] + 153
12  QuartzCore                          0x0000000117f4c4dd _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 401
13  QuartzCore                          0x0000000117ed4ded _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 365
14  QuartzCore                          0x0000000117f00704 _ZN2CA11Transaction6commitEv + 500
15  QuartzCore                          0x0000000117f01450 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76
16  CoreFoundation                      0x00000001122f8d37 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
17  CoreFoundation                      0x00000001122f8c8e __CFRunLoopDoObservers + 430
18  CoreFoundation                      0x00000001122dc9db CFRunLoopRunSpecific + 443
19  GraphicsServices                    0x000000011780c9c6 GSEventRunModal + 62
20  UIKit                               0x0000000114ba35e8 UIApplicationMain + 159
21  ExponentiallyMe                     0x000000010f891167 main + 55
22  libdyld.dylib                       0x0000000113b54d81 start + 1
23  ???                                 0x0000000000000002 0x0 + 2

The error is being thrown in the call [_UIQueuingScrollView _setWrappedViewAtIndex:withView:] + 377 by the looks of it, and I have no idea why. Below is also the code from the viewDidLoad() and the two required methods of the UIPageViewControllerDataSource Protocol.

viewDidLoad()

override func viewDidLoad() {
    super.viewDidLoad()

    self.dataSource = self
    self.delegate = self

    self.activityIndicator("Loading Survey.")

    self.pageControl.frame = CGRect()
    self.pageControl.currentPageIndicatorTintColor = UIColor.white
    self.pageControl.pageIndicatorTintColor = UIColor.lightGray
    self.pageControl.currentPage = 0
    self.view.addSubview(self.pageControl)

    self.pageControl.translatesAutoresizingMaskIntoConstraints = false
    self.pageControl.bottomAnchor.constraint(equalTo: self.view.topAnchor, constant: -20).isActive = true
    self.pageControl.widthAnchor.constraint(equalTo: self.view.widthAnchor, constant: -20).isActive = true
    self.pageControl.heightAnchor.constraint(equalToConstant: 20).isActive = true
    self.pageControl.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true

    if self.selectedSurveyType == EMSurveyType.EMST_VISA {
        self.pageControl.numberOfPages = 12
    } else {
        self.pageControl.numberOfPages = 2
    }


    setViewControllers([self.instructionView], direction: .forward, animated: true, completion: nil)
}

UIPageViewControllerDataSource Protocol Methods

// Get the previous ViewController
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
    guard self.currentPageIndex - 1 > 0 else {
        if self.currentPageIndex - 1 == 0 {
            self.currentPageIndex = 0
            return self.instructionView
        } else {
            return nil
        }
    }
    self.currentPageIndex -= 1
    return self.questionViewControllerAtIndex(self.currentPageIndex - 1)
}

// Get the next ViewController
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
    guard self.currentPageIndex + 1 <= self.questionCount else {
        if self.currentPageIndex == self.questionCount {
            self.currentPageIndex = self.questionCount + 1
            return completionView
        } else {
            return nil
        }
    }
    self.currentPageIndex += 1
    return self.questionViewControllerAtIndex(self.currentPageIndex - 1)
}

It might also be helpful that the subclasses UIPageViewController is presented modally from a UIViewController in a UITabBarController. Furthermore, the questionViewControllers are created in the function:

func questionViewControllerAtIndex(_ index: Int) -> UIViewController {
    let storyboard = UIStoryboard(name:"Main", bundle: nil)
    let qVC = storyboard.instantiateViewController(withIdentifier: "EMSQuestionViewController") as! EMSurveyQuestionViewController
    qVC.questionProperties(questionStatement: self.mainSurveyQuestions[index], subStatements: self.subStatementsForSurveyQuestions[index])
    return qVC
}

Thank you very much in advance, I am extremely lost on this one, and don't understand what could be causing the issues. Any help or "pointing-me-in-the-right-direction" is greatly appreciated, and if you need any further information/code please let me know.


Solution

  • So to anyone reading this later on, I seem to have solved it. I'm not quite sure how this approach works but not the previous one (if someone could enlighten me, that would be great), but it seems like trying to track the current page index and create the view controllers on the fly doesn't work. I rewrote the viewDidLoad() to create an array allPages, that holds all the pages before hand. Then in the two protocol methods, instead of checking the self.currentPageIndex for the current position, I evaluate the current index based on the passed viewController instance and check in the allPages array for the index using allPages.index(of: viewController). Based on that value I check and then pass the view controller required back by either increasing or decreasing the value of index by 1. E.g. for the previous, the return is based on return allPages[index! - 1]. I hope this might help someone stumbling on this question in the future, and if someone could enlighten me on why this works, but not my previous approach, that would also be great.