UIPageViewController转换'非平衡调用开始/结束外观转换为'

当我通过UIPageViewController比它的转换animation更快地导航时,我得到了“ Unbalanced calls to begin/end appearance transitions for <MyDataViewController>Unbalanced calls to begin/end appearance transitions for <MyDataViewController> ”,并且直到我尝试翻页为止, Unbalanced calls to begin/end appearance transitions for <MyDataViewController>中的两个视图之一才显示出来。

任何人有解决这个错误的想法?

上面的答案是对的,但是我认为比所需要的更详细,而且食谱是有帮助的。 所以这里似乎是为我工作:

在设置和调用pageViewController的视图控制器中,声明:

 @property (assign) BOOL pageIsAnimating; 

并在viewDidLoad:

  pageIsAnimating = NO; 

添加这个:

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

并添加几行:

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

通过拒绝提供视图控制器信息来抑制手势:

 - (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; } 

哦,方向改变重置标志:

 - (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation { pageIsAnimating = NO; ... } 

解决以下步骤:
1-声明一个标志来表示animation已经完成或没有完成:

 BOOL pageAnimationFinished; 

2-在viewDidLoad中将此标志设置为true:

 pageAnimationFinished = YES; 

3-为pageViewController禁用tapGesture并将“self”分配给panGestureRecognizer委托:

 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-通过以下手势识别器委托方法允许/禁止panGestureRecognizer:

 -(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-添加下面的pageViewController委托方法:

 - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { pageAnimationFinished = YES; } 

来自Basem Saadawy的良好回答 ,但它有一些缺陷。

其实代表的gestureRecognizerShouldBegin:可以被调用,没有进一步的animation开始。 如果通过垂直手指的移动开始手势,并且水平偏移不足以启动animation(但足以启动gestureRecognizerShouldBegin:),则可以这样 。 因此,我们的variablespageAnimationFinished将被设置为NO没有实际的animation。 因此, pageViewController:didFinishAnimating:将永远不会被调用,并且您将冻结当前页面而无法更改它。

这就是为什么把NO赋给这个variables的一个更好的地方是一个手势识别器的动作方法,检查它的速度和转换(我们只对水平方向感兴趣)。

所以最后的步骤是:

1)声明一个实例variables(一个标志):

 BOOL pageAnimationFinished; 

2)设置其初始值

 - (void)viewDidLoad { [super viewDidLoad]; ... pageAnimationFinished = YES; } 

3)为平移手势识别器分配委托和自定义操作

 for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers) { if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]]) { gesRecog.delegate = self; [gr addTarget:self action:@selector(handlePan:)]; } } 

3')当手势的平移水平方向较大,手指水平移动时,真正开始animation。
我猜在UIPageViewController分配的内部识别器的动作中使用了相同的逻辑。

 - (void) handlePan:(UIPanGestureRecognizer *)gestureRecognizer { if (pageAnimationFinished && gestureRecognizer.state == UIGestureRecognizerStateChanged) { CGPoint vel = [gestureRecognizer velocityInView:self.view]; CGPoint tr = [gestureRecognizer translationInView:self.view]; if (ABS(vel.x) > ABS(vel.y) && ABS(tr.x) > ABS(tr.y)) pageAnimationFinished = NO; // correct place } } 

4)如果animation未完成,则禁止手势。

 -(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; } return YES; } 

5)animation完成

 - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { pageAnimationFinished = YES; } 

我玩得太多了,似乎这是一个很好的解决scheme,效果很好。

以下是使用委托的QUICK版本:

添加这段代码(确保你的头文件或类扩展中包含了UIPageViewControllerDelegate,并赋值self.pageViewController.delegate = self; ):

 - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers { self.pageAnimationFinished = NO; } - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { self.pageAnimationFinished = YES; } 

然后检查self.pageAnimationFinished并返回nil,如果它是== NO

较长的说明:

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

我们可以使用UIPageViewControllerDelegate这个委托方法来知道从页面翻转或者滑动完成的animation何时完成。 使用这个我们可以像这样实现它:

 - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { pageAnimationFinished = YES; } 

那么,只需返回nil

 - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(PageViewController *)viewController 

 - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(PageViewController *)viewController 

什么时候

pageAnimationFinished == NO 。 animation时一定要将pageAnimationFinished设置为NO 。 知道何时使用animation的最好方法是使用相反的方法

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

即:

 - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers 

从那以后,我一直没有看到这种警告,这可以用其他解决scheme的三分之一来完成。 而且更容易遵循。

Bill Cheswick的答案的Swift版本(现在是最高的答案):

添加一个variables来保持当前状态:

 var pageIsAnimating = false 

设置animation状态:

 func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) { self.pageIsAnimating = true } func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if finished || completed { self.pageIsAnimating = false } } 

如果目前正在进行animation制作,则阻止转场:

 func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? { if self.pageIsAnimating { return nil } // Your code here } func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { if self.pageIsAnimating { return nil } // Your code here } 

谢谢Bill Cheswick!

我的解决scheme迅速,简单,有效:

  1. 将pageviewcontroller委托设置为您的类
  2. 添加下面的代码

     extension MyPageVC: UIPageViewControllerDelegate { func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { self.view.isUserInteractionEnabled = false } func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { self.view.isUserInteractionEnabled = true } } 

这个怎么样:

 - (void)pageViewController:(UIPageViewController*)pgVC willTransitionToViewControllers:(NSArray*)pendingVCs { pgVC.dataSource = nil; // ... to disallow user to change pages until animation completes } - (void)pageViewController:(UIPageViewController*)pgVC didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray*)prevVCs transitionCompleted:(BOOL)completed { if(completed || finished) { pgVC.dataSource = _pgViewDataSource; // ... to allow user to change pages again } } 

利用你的UIPageViewControllerDelegate方法并设置警戒,防止在检测到过多的页面转动时创build新的页面视图。

  1. 您可以禁用手势识别器
  2. 将“userInteraction”设置为在UIView上禁用
  3. 在UIPageViewController上维护一个标志,以便在出现animation时忽略进一步的input。 (关于这个选项的警告.. ios5和ios6有不同的方式来确定何时animation开始..)

我不得不把它添加到viewDidAppear:使其工作

  - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.pageAnimationFinished = YES; } 

我将尝试在转换时忽略UIPageViewControllers上的手势。