ios - UIPageViewController 手势正在调用 viewControllerAfter : but doesn't animate

标签 ios uipageviewcontroller

我有一个关于 UIPageViewController 的非常有趣的问题。

我的项目设置与基于页面的应用程序模板示例非常相似。 时不时地(但在一定程度上可重现)某个平移手势会调用 -(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController

我返回下一页的 View Controller ,但从未运行过翻页动画,也从未调用过我的委托(delegate)方法。

这是viewControllerAfterViewController的代码

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    PageDisplayViewController *vc = (PageDisplayViewController *)viewController;
    NSUInteger index = [self.pageFetchController.fetchedObjects indexOfObject:vc.page];
    if(index == (self.pageFetchController.fetchedObjects.count - 1)) return nil;
    return [self getViewControllerForIndex:(++index)];
}

这是 getViewControllerForIndex:

-(PageDisplayViewController *)getViewControllerForIndex:(NSUInteger)index
{
    PageDisplayViewController *newVC = [self.storyboard instantiateViewControllerWithIdentifier:@"PageDisplayController"];
    newVC.page = [self.pageFetchController.fetchedObjects objectAtIndex:(index)];
    newVC.view.frame = CGRectMake(0, 0, 1024, 604);
    NSLog(@"%i", index);
    if(index == 0)
    {
        //We're moving to the first, animate the back button to be hidden.
        [UIView animateWithDuration:0.5 animations:^
        {
            self.backButton.alpha = 0.f;
        } completion:^(BOOL finished){
            self.backButton.hidden = YES;
        }];
    }
    else if(index == (self.pageFetchController.fetchedObjects.count - 1))
    {
        [UIView animateWithDuration:0.5 animations:^{
            self.nextButton.alpha = 0.f;
        } completion:^(BOOL finished){
            self.nextButton.hidden = YES;
        }];
    }
    else
    {
        BOOL eitherIsHidden = self.nextButton.hidden || self.backButton.hidden;
        if(eitherIsHidden)
        {
            [UIView animateWithDuration:0.5 animations:^{
                if(self.nextButton.hidden)
                {
                    self.nextButton.hidden = NO;
                    self.nextButton.alpha = 1.f;
                }
                if(self.backButton.hidden)
                {
                    self.backButton.hidden = NO;
                    self.backButton.alpha = 1.f;
                }
            }];
        }
    }
    return newVC;
}

基本上,我创建 View Controller ,设置它的数据对象,然后根据索引淡出下一步/后退按钮。

委托(delegate)方法

-(void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    PageDisplayViewController *vc = [previousViewControllers lastObject];
    NSUInteger index = [self.pageFetchController.fetchedObjects indexOfObject:vc.page];

    if (!completed)
    {
        [self.pagePreviewView setCurrentIndex:index];
        NSLog(@"Animation Did not complete, reverting pagepreview");
    }
    else
    {
        PageDisplayViewController *curr = [pageViewController.viewControllers lastObject];
        NSUInteger i = [self.pageFetchController.fetchedObjects indexOfObject:curr.page];
        [self.pagePreviewView setCurrentIndex:i];
        NSLog(@"Animation compeleted, updating pagepreview. Index: %u", i);
    }
}

我只是注意到这个问题,因为我的后退按钮会随机地重新出现在屏幕上。在其中加入一些 NSLog() 语句后,我注意到我的 dataSource 方法被调用以获得索引 1,但是没有动画播放或委托(delegate)被调用。更可怕的是,如果我尝试平移下一页,索引 1 将再次被调用。

我担心这可能是 UIPageViewController 的错误。

最佳答案

由于我在第一个答案中仍然收到神秘的实现崩溃,我一直在寻找一个“足够好”的解决方案,该解决方案较少依赖于对页面 View Controller (PVC) 底层行为的个人假设。这是我想出的办法。

我以前的方法有点侵入性,更像是一种变通方法,而不是可接受的解决方案。与其对抗 PVC 以迫使它做我认为它应该做的事情,不如接受这样的事实:

  • pageViewController:viewControllerBeforeViewController:pageViewController:viewControllerAfterViewController: 方法可以被 UIKit 调用任意次数,并且
  • 绝对不能保证其中任何一个对应于分页动画,也不保证它们之后会调用 pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:

这意味着我们不能将 before/after 方法用作“动画开始”(但是请注意,didFinishAnimating 仍然用作“动画结束”事件)。那么我们怎么知道动画确实开始了呢?

根据我们的需要,我们可能对以下事件感兴趣:

  1. 用户开始摆弄页面: 一个很好的指标是前/后回调,或者更准确地说是第一个回调。

  2. 翻页手势的第一个视觉反馈: 我们可以在 PVC 的点击和平移手势识别器的 state 属性上使用 KVO。当观察到平移的 UIGestureRecognizerStateBegan 值时,我们可以非常确定视觉反馈将随之而来。

  3. 用户通过释放触摸来完成拖动页面: 再次,KVO。当 UIGestureRecognizerStateRecognized 值被报告用于平移或点击时,PVC 将实际翻页,因此这可以用作“动画开始”。

    <
  4. UIKit 启动分页动画: 我不知道如何获得对此的直接反馈。

  5. UIKit 结束分页动画: 小菜一碟,只听 pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:

对于 KVO,只需抓取 PVC 的手势识别器,如下所示:

@interface MyClass () <UIGestureRecognizerDelegate>
{
    UIPanGestureRecognizer* pvcPanGestureRecognizer;
    UITapGestureRecognizer* pvcTapGestureRecognizer;
}
...
for ( UIGestureRecognizer* recognizer in pageViewController.gestureRecognizers )
{
    if ( [recognizer isKindOfClass:[UIPanGestureRecognizer class]] )
    {
        pvcPanGestureRecognizer = (UIPanGestureRecognizer*)recognizer;
    }
    else if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] )
    {
        pvcTapGestureRecognizer = (UITapGestureRecognizer*)recognizer;
    }
}

然后将您的类注册为 state 属性的观察者:

[pvcPanGestureRecognizer addObserver:self
                          forKeyPath:@"state"
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
[pvcTapGestureRecognizer addObserver:self
                          forKeyPath:@"state"
                             options:NSKeyValueObservingOptionNew
                             context:NULL];

并实现通常的回调:

- (void)observeValueForKeyPath:(NSString *)keyPath
        ofObject:(id)object
        change:(NSDictionary *)change
        context:(void *)context
{
    if ( [keyPath isEqualToString:@"state"] && (object == pvcPanGestureRecognizer || object == pvcTapGestureRecognizer) )
    {
        UIGestureRecognizerState state = [[change objectForKey:NSKeyValueChangeNewKey] intValue];
        switch (state)
        {
            case UIGestureRecognizerStateBegan:
            // trigger for visual feedback
            break;

            case UIGestureRecognizerStateRecognized:
            // trigger for animation-begin
            break;

            // ...
        }
    }
}

完成后,不要忘记取消订阅这些通知,否则您的应用可能会出现泄漏和奇怪的崩溃:

[pvcPanGestureRecognizer removeObserver:self
                             forKeyPath:@"state"];
[pvcTapGestureRecognizer removeObserver:self
                             forKeyPath:@"state"];

这就是所有人!

关于ios - UIPageViewController 手势正在调用 viewControllerAfter : but doesn't animate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12533240/

相关文章:

ios - 这个返回参数是什么意思?

ios - 对同一 NSManagedObjectContext 对象的多个并发 perform() 调用

ios - UIButton 在 iOS7 中的 UIPageViewController 中的 ViewController 中没有突出显示

swift - 如何使 ScrollView 内的 TableView 的滚动行为自然

ios - UIPageViewController 滑动延迟

ios - 为什么需要 instantiateViewController?

objective-c - 应用程序代码签名验证失败 - 又一次?

ios - 在 iOS 中切换 View 的最佳方式

ios - UIPageViewController 第二页和第三页闪烁为白色

ios - 如何在 UIPageViewController 后面添加背景视频 View ?