Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pageViewController setViewControllers crashes with "Invalid parameter not satisfying: [views count] == 3"

Tags:

xcode

I know that there are several other questions like this, but i couldn't find a solution for my problem. I use a pageViewController which displays different ViewControllers. Every time the pageViewController moves forward I check the input of the lastPage. If it's wrong the pageViewController should go back to that page by using the setViewController method. Without this method everything works fine but if I try to use it the app crashes with the following exception:

19:48:25.596 Phook[23579:60b] *** Assertion failure in -[_UIQueuingScrollView _replaceViews:updatingContents:adjustContentInsets:animated:], /SourceCache/UIKit_Sim/UIKit-2935.137/_UIQueuingScrollView.m:383  
2014-06-02 19:48:25.600 Phook[23579:60b] *** Terminating app due to uncaught   exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying:   [views count] == 3'

And here's my code:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished
   previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    if (completed && finished)
    {

        RegisterUserPageContent* lastPage = (RegisterUserPageContent*)previousViewControllers[ previousViewControllers.count-1];
        int lastIndex   = lastPage.pageIndex;
        int newIndex    = ((RegisterUserPageContent*)[self.pageViewController.viewControllers objectAtIndex:0]).pageIndex;

        if (newIndex > lastIndex)
        {   // Moved Forward
            if(!lastPage.testInput)
            {
                [self.pageViewController setViewControllers:@[ [self.storyboard instantiateViewControllerWithIdentifier:_pageStoryBoardIDs[0]] ]
                                         direction:UIPageViewControllerNavigationDirectionReverse
                                         animated:YES completion:nil];


            }
        }
        else
        {   // Moved Reverse

        }
    }
}

As I already said. I searched a lot and implemented some solutions but nothing helped. Thanks.

like image 223
Björn Avatar asked Jun 02 '14 18:06

Björn


3 Answers

I ran into the same problem, and was able to solve it using a tip from this answer https://stackoverflow.com/a/20973822/3757370. Simply placing the setViewControllers:direction:animated:completion: code inside of a dispatch_async block on the main queue fixed it for me. For you this would look like

dispatch_async(dispatch_get_main_queue(), ^{
    [self.pageViewController setViewControllers:@[ [self.storyboard instantiateViewControllerWithIdentifier:_pageStoryBoardIDs[0]] ]
                                     direction:UIPageViewControllerNavigationDirectionReverse
                                     animated:YES completion:nil];
});

Hope it helps!

like image 52
jpecoraro342 Avatar answered Nov 19 '22 17:11

jpecoraro342


Ran into this exact same issue. Solved it by setting the first content controller explicitly on the UIPageViewController when it is first loaded (ie: inside 'viewDidLoad').

// create first page
UIViewController* firstContentPageController = [self contentControllerAtIndex: 0];
[_paginationController
    setViewControllers: @[firstContentPageController]
    direction: UIPageViewControllerNavigationDirectionForward
    animated: NO
    completion: nil];

where 'contentControllerAtIndex:' is a simple helper method that creates a content controller. I also use it within the two delegate methods in order to return the appropriate controller for a given page.

- (UIViewController*)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
    NSInteger index = (NSInteger)((MyContentController*)viewController).pageIndex;

    return [self contentControllerAtIndex: index - 1];
}


- (UIViewController*)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
    NSInteger index = (NSInteger)((MyContentController*)viewController).pageIndex;

    return [self contentControllerAtIndex: index + 1];
}


- (MyContentController*)contentControllerAtIndex: (NSInteger)index {
    if (index < 0 || _pagesContent.count <= index)
        return nil;

    // create view controller
    MyContentController* contentController = [self.storyboard instantiateViewControllerWithIdentifier: @"MyContentController"];
    contentController.pageIndex = index;
    contentController.content = [_pagesContent objectAtIndex: index];

    return contentController;
}

The reason for this is that the UIPageViewControllerDataSource protocol was designed to only have methods for pulling the previous/next content controller, as opposed to pulling a single controller at a particular index. It's an odd design decision, but the caveat is that instead of being called by Cocoa when the component is first loaded you have to manually set its starting state. Poor framework design here IMHO.

like image 9
Marchy Avatar answered Nov 19 '22 17:11

Marchy


Well it's 2018 and the bug is still around. I tried all the solutions above, but none worked for me. And after many tries, setting the animation parameter to false, was what worked for me in swift 4

let myViewController : UIViewController = orderedViewControllers[vcIndex]
setViewControllers([myViewController], direction: .forward, animated: false, completion: nil);

Then it was just a matter of setting a custom transition. Hope it helps others in the same situation.

like image 6
kax Avatar answered Nov 19 '22 17:11

kax