Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIPageViewController transition 'Unbalanced calls to begin/end appearance transitions for '

When I navigate through UIPageViewController faster than its transition animation I am getting 'Unbalanced calls to begin/end appearance transitions for <MyDataViewController>' and one of the two views in landscape isn't shown until I try to turn the page.

Anybody has an idea to solve this bug?

like image 592
Basem Saadawy Avatar asked Nov 06 '12 09:11

Basem Saadawy


2 Answers

Solved following these steps:
1- Declare a flag to indicate that the animation has finished or not:

BOOL pageAnimationFinished;

2- Set this flag to true in viewDidLoad:

pageAnimationFinished = YES;

3- Disable tapGesture for the pageViewController and assign 'self' to panGestureRecognizer delegate:

for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
    if ([gesRecog isKindOfClass:[UITapGestureRecognizer class]])
        gesRecog.enabled = NO;
    else if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
        gesRecog.delegate = self;
}

4- Allow/Disallow panGestureRecognizer through the following gesture recognizer delegate method:

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
    {
        UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
        if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
            return NO;
        pageAnimationFinished = NO;
    }
    return YES;
}

5- Add the following pageViewController delegate method:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    pageAnimationFinished = YES;
}
like image 33
Basem Saadawy Avatar answered Sep 28 '22 07:09

Basem Saadawy


The above answers were right, but I think more elaborate than needed, and cookbook is helpful. So here is what seems to be working for me:

In the view controller that sets up and calls the pageViewController, declare:

@property (assign)              BOOL pageIsAnimating;

and in viewDidLoad:

    pageIsAnimating = NO;

add this:

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
    pageIsAnimating = YES;
}

and add a couple of lines to:

- (void)pageViewController:(UIPageViewController *)pageViewController
    didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers
   transitionCompleted:(BOOL)completed {
    if (completed || finished)   // Turn is either finished or aborted
        pageIsAnimating = NO;
    ...
}

The gestures are suppressed by declining to provide view controller information:

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
   viewControllerAfterViewController:(UIViewController *)viewController {
    if (pageIsAnimating)
        return nil;
    ...
    return after;
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
  viewControllerBeforeViewController:(UIViewController *)viewController {
    if (pageIsAnimating)
        return nil;
    ...
    return before;
}

Oh, and orientation changes reset the flag:

- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
               spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
    pageIsAnimating = NO;
    ...
}
like image 166
Bill Cheswick Avatar answered Sep 28 '22 06:09

Bill Cheswick