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

时间:2014-05-06 00:57:16

标签: ios objective-c uinavigationcontroller uikit

如果您拖动UIViewController以开始UINavigationController内的互动式广告投放转换,则当前版本下方的UIViewController会调用viewWillAppear:,然后是{{ 1}}方法UINavigationControllerDelegate

如果取消转换,则会按预期在顶视图控制器上调用navigationController:willShowViewController:animated:viewWillAppear:

但是,根本没有调用任何委托方法viewDidAppear:navigationController:willShowViewController:animated:

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

我真正需要的是能够在我的UINavigationController子类或UINavigationController内查看交互式流行音乐的取消时间。有明显的方法吗?

修改

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

EDIT2

我的雷达车票(16823313)今日(2015年5月21日)已关闭并标记为预期。 :(

  

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

     

这实际上是正确的行为。导航过渡   发生在B - > A,如果你在转型过程中取消它,你   不会得到didShowViewController:方法。取消这个   转换不应被视为从A - >转换; B因为   你从来没有真正达到过A。

     

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

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

3 个答案:

答案 0 :(得分:34)

对于任何有兴趣的人,我找到了两种方法在UINavigationControllerDelegate级别解决此问题。

  1. 使用KVO观察state的{​​{1}}属性。不幸的是,取消转换并不会将状态更改为interactivePopGestureRecognizer,而只是UIGestureRecognizerStateFailed,因此您需要编写一些额外的代码来跟踪发生的情况,如果您需要识别已取消的或者完成了流行音乐。

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

    navigationController:willShowViewController:animated:
  3. 我最初对使用此解决方案犹豫不决,因为文档不清楚是否可以设置多个这些(因为在这种情况下,如果一个未知的视图控制器也设置了自己的通知块,它可能会要么替换这个,要么替换这个。经过测试后,看起来它不是1:1的关系,你可以安全地添加多个通知块。

    修改

    我编辑了上面的代码,将- (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]; }); } } }]; } 调用延迟到仅在完成动画时调用,以更接近地匹配预期的默认行为。

答案 1 :(得分:3)

斯威夫特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)
        }
    }
}

答案 2 :(得分:0)

我将@ Dima&#39; s answer翻译成Swift用于我的项目:

func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {

    transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock { context in
        guard context.isCancelled(), let fromViewController = context.viewControllerForKey(UITransitionContextFromViewControllerKey) else { return }

        self.navigationController(self, willShowViewController: fromViewController, animated: animated)

        let animationCompletion: NSTimeInterval = context.transitionDuration() * Double(context.percentComplete())

        let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
        dispatch_after(delayTime, dispatch_get_main_queue()) {
            self.navigationController(self, didShowViewController: fromViewController, animated: animated)
        }            
    }

    /* Your normal behavior goes here */

}

请注意,我没有检查是否存在navigationController(_:didShowViewController:animated:)的实现,虽然我相信在编译时会在Swift中检查这一点,并且如果在编译时出现编译错误,你试图在没有实现的情况下调用它。