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

时间:2012-11-06 09:42:05

标签: ios transition uipageviewcontroller

当我快速浏览UIPageViewController过渡动画时,我会收到“Unbalanced calls to begin/end appearance transitions for <MyDataViewController>”,并且在尝试翻页之前,横向中的两个视图中的一个不会显示。

有人有想法解决这个错误吗?

10 个答案:

答案 0 :(得分:28)

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

在设置和调用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 :(得分:9)

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

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

答案 2 :(得分:6)

来自Basem Saadawy的好answer,但它有一些缺陷。

实际上可以调用委托的 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;
}

我玩得太多了,看起来这是一个很好的解决方案。

答案 3 :(得分:6)

这是使用委托的快速版本:

添加此代码(确保在标题或类扩展中包含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。制作动画时,请务必将pageAnimationFinished设置为NO。了解动画时间的最佳方法是使用与

相反的方法
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed

即:

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

从那时起我就没有看过这个警告,这可以在其他解决方案的1/3行中完成。而且它更容易理解。

答案 4 :(得分:4)

以下是比尔·切斯威克的答案的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
}

谢谢Bill Cheswick!

答案 5 :(得分:2)

我的解决方案迅速,简单且有效:

  1. 将pageviewcontroller委托设置为您的班级
  2. 添加以下代码

    public class B extends A implements Closeable{
        @Override
        public boolean close() throws IOException {
            throw new UnsupportedOperationException("Not supported yet."); 
        }
    }
    

答案 6 :(得分: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
    }
}

答案 7 :(得分:0)

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

  1. 您可以停用手势识别器
  2. 设置&#34; userInteraction&#34;在UIView上禁用
  3. 在UIPageViewController上维护一个标志,以便在发生动画时忽略进一步的输入。 (关于此选项的警告.. ios5和ios6有不同的方法来确定动画何时开始..)

答案 8 :(得分:0)

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

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

答案 9 :(得分:-2)

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