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.
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.