19

UIPageViewController当我以比其过渡动画更快的速度导航时,我得到' Unbalanced calls to begin/end appearance transitions for <MyDataViewController>',并且在我尝试翻页之前,不会显示横向的两个视图之一。

有人有解决此错误的想法吗?

4

10 回答 10

29

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

在设置和调用 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;
    ...
}
于 2013-02-11T22:44:45.820 回答
9

按照以下步骤解决:
1-声明一个标志以指示动画是否已完成:

BOOL pageAnimationFinished;

2- 在 vi​​ewDidLoad 中将此标志设置为 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;
}
于 2013-01-13T15:43:50.950 回答
6

Basem Saadawy 的 回答很好,但它有一些缺陷。

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

这就是为什么将NO分配给这个变量的更好的地方是手势识别器的动作方法,检查它的速度和平移(我们只对水平方向感兴趣)。

所以最后的步骤是:

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

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') 当手势在水平方向上的平移更大并且手指在某一时刻水平移动时,动画才真正开始。我猜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) 如果动画未完成,则禁止手势。

-(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) 动画完成

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

我玩得太多了,似乎这是一个很好的解决方案。

于 2013-05-06T10:03:18.217 回答
6

这是使用委托的 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来了解翻页或滑动页面的动画何时结束。使用它我们可以像这样实现它:

- (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. 一定要设置pageAnimationFinishedNO动画时。了解何时制作动画的最佳方法是使用相反的

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

即:

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

从那以后我就再也没有看到过这个警告,这可以像其他解决方案一样在 1/3 的行中完成。而且它更容易遵循。

于 2014-04-04T17:20:58.927 回答
4

这是 Bill Cheswick 答案的 Swift 版本(目前是最佳答案):

添加一个变量来保存当前状态:

var pageIsAnimating = false

设置动画状态:

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

如果当前正在制作动画,则阻止过渡:

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
}

谢谢比尔·切斯维克!

于 2016-05-31T10:06:36.910 回答
3

我的解决方案快速、简单且有效:

  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
        }
    }
    
于 2016-07-22T08:59:23.620 回答
1

这个怎么样:

- (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
    }
}
于 2014-01-10T19:35:19.147 回答
0

使用您的 UIPageViewControllerDelegate 方法并设置保护措施,以防止在检测到过多的翻页时创建新的页面视图。

  1. 您可以禁用手势识别器
  2. 在 UIView 上将“userInteraction”设置为禁用
  3. 在 UIPageViewController 上维护一个标志,以在发生动画时忽略进一步的输入。(关于这个选项的警告.. ios5 和 ios6 有不同的方法来确定动画何时开始..)
于 2013-01-09T17:53:00.343 回答
0

我必须将它添加到 viewDidAppear: 以使其工作

    - (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.pageAnimationFinished = YES;
}
于 2013-10-22T15:10:03.760 回答
-2

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

于 2012-11-06T14:17:51.563 回答