取消交互式UINavigationController pop手势不会调用UINavigationControllerDelegate方法

Dim*_*ima 20 objective-c uikit uinavigationcontroller ios

如果你在a中拖动UIViewController以开始a 中的交互式弹出过渡UINavigationController,UIViewController则当前的一个已经viewWillAppear:调用,然后是UINavigationControllerDelegate方法navigationController:willShowViewController:animated:

如果取消的过渡,既viewWillAppear:viewDidAppear:预期得到所谓的顶视图控制器.

然而,无论是委托方法navigationController:willShowViewController:animated:navigationController:didShowViewController:animated:调用的.

考虑到调用UIViewController视图生命周期方法,似乎至少应调用其中一个或两个.我想知道这是故意还是错误UIViewController.

我真正需要的是能够看到在我的UINavigationController子类或其中取消交互式弹出的时间UINavigationController.有明显的方法吗?

编辑

我仍在寻找解决方案,但我想提一下,我已将此问题报告为Apple的错误.查看文档,没有理由不调用这些委托方法,特别是考虑到调用等效的视图生命周期方法.

EDIT2

我的雷达车票(16823313)今天(2015年5月21日)关闭并按预定标记.:(

Engineering已根据以下信息确定此问题的行为符合预期:

这实际上是正确的行为.从B - > A发生的导航转换,如果你在转换中取消它,你将不会得到didShowViewController:方法.取消这种转变不应被视为从A - > B过渡,因为你从未真正到过A.

view [Will/Did]出现仍应按预期调用.

这种情况相当糟糕,因为它是违反直觉的,但我的答案中的解决方法在可预见的未来应该可以正常工作,至少在我的用例中是这样.

Dim*_*ima 34

对于任何有兴趣的人,我已经找到了两种方法来解决这个问题UINavigationControllerDelegate.

  1. 用KVO来观察它的state性质interactivePopGestureRecognizer.不幸的是,取消转换并不会改变状态,UIGestureRecognizerStateFailed而只是改变状态UIGestureRecognizerStateEnded,所以如果需要识别已取消或已完成的弹出窗口,则需要编写一些额外的代码来跟踪发生的情况.

  2. 测试之后,这可能是更好的解决方案:使用该navigationController:willShowViewController:animated:方法向转换协调器添加通知块.它看起来像这样:

    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
    {
        [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context)
        {
            if([context isCancelled])
            {
                UIViewController *fromViewController = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
                [self navigationController:navigationController willShowViewController:fromViewController animated:animated];
    
                if([self respondsToSelector:@selector(navigationController:didShowViewController:animated:)])
                {
                    NSTimeInterval animationCompletion = [context transitionDuration] * (double)[context percentComplete];
    
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((uint64_t)animationCompletion * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        [self navigationController:navigationController didShowViewController:fromViewController animated:animated];
                    });
                }
    
    
            }
        }];
    }
    
    Run Code Online (Sandbox Code Playgroud)

我最初使用这个解决方案时犹豫不决,因为文档不清楚你是否可以设置多个这样的解决方案(因为在这种情况下,如果一个不知情的视图控制器也设置了自己的通知块,它可能会替换它一个或被这一个取代).经过测试后,它似乎不是1:1的关系,您可以安全地添加多个通知块.

编辑

我编辑了上面的代码,以延迟navigationController:didShowViewController:animated:调用只在动画完成时才调用,以更接近地匹配预期的默认行为.


Kur*_*t J 7

斯威夫特3:

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    transitionCoordinator?.notifyWhenInteractionEnds { context in
        guard context.isCancelled, let fromViewController = context.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
        self.navigationController(self, willShow: fromViewController, animated: animated)
        let animationCompletion: TimeInterval = context.transitionDuration * Double(context.percentComplete)
        DispatchQueue.main.asyncAfter(deadline: .now() + animationCompletion) {
            self.navigationController(self, didShow: fromViewController, animated: animated)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)