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

Bas*_*awy 18 transition ios uipageviewcontroller

当我浏览UIPageViewController速度超过其过渡动画时,我得到了' Unbalanced calls to begin/end appearance transitions for <MyDataViewController>',并且在我尝试翻页之前,横向中的两个视图中的一个未显示.

有人有想法解决这个bug吗?

Bil*_*ick 28

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

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

@property (assign)              BOOL pageIsAnimating;
Run Code Online (Sandbox Code Playgroud)

并在viewDidLoad中:

    pageIsAnimating = NO;
Run Code Online (Sandbox Code Playgroud)

添加这个:

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
    pageIsAnimating = YES;
}
Run Code Online (Sandbox Code Playgroud)

并添加几行:

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

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

- (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;
}
Run Code Online (Sandbox Code Playgroud)

哦,方向更改重置标志:

- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
               spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
    pageIsAnimating = NO;
    ...
}
Run Code Online (Sandbox Code Playgroud)


Bas*_*awy 9

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

BOOL pageAnimationFinished;
Run Code Online (Sandbox Code Playgroud)

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

pageAnimationFinished = YES;
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

5-添加以下pageViewController委托方法:

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


小智 6

答案来自Basem Saadawy但是它有一些缺陷.

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

这就是为什么一个更好的地方来分配NO这个变量是一个手势识别与它的速度和翻译的检查操作方法(我们感兴趣的只有水平方向).

所以最后的步骤是:

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

BOOL pageAnimationFinished;
Run Code Online (Sandbox Code Playgroud)

2)设置其初始值

- (void)viewDidLoad
{
    [super viewDidLoad];
    ...
    pageAnimationFinished = YES;
}
Run Code Online (Sandbox Code Playgroud)

3)为平移手势识别器分配代理和自定义动作

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

5)动画结束

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

我用它玩了太多,似乎这是一个很好的解决方案.


Oxc*_*cug 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;
}
Run Code Online (Sandbox Code Playgroud)

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

更长的解释:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
Run Code Online (Sandbox Code Playgroud)

我们可以使用此委托方法UIPageViewControllerDelegate来了解翻页或滑动页面的动画何时结束.使用这个,我们可以像这样实现它:

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

然后,回到nil你的

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(PageViewController *)viewController
Run Code Online (Sandbox Code Playgroud)

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(PageViewController *)viewController
Run Code Online (Sandbox Code Playgroud)

什么时候

pageAnimationFinished == NO.一定要设置pageAnimationFinishedNO当你动画.了解动画时间的最佳方法是使用相反的方法

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
Run Code Online (Sandbox Code Playgroud)

即:

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
Run Code Online (Sandbox Code Playgroud)

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