取消交互式UINavigationControllerpopup手势不会调用UINavigationControllerDelegate方法
如果拖动UIViewController
以在UINavigationController
开始交互式popup转换,则当前窗口下方的UIViewController
具有viewWillAppear:
called,随后是UINavigationControllerDelegate
方法navigationController:willShowViewController:animated:
如果取消转换, viewWillAppear:
预期方式在顶部视图控制器上调用viewWillAppear:
和viewDidAppear:
但是, navigationController:willShowViewController:animated:
委托方法navigationController:willShowViewController:animated:
或navigationController:didShowViewController:animated:
至less应该调用其中的一个或两个,考虑调用UIViewController视图生命周期方法。 我想知道这是故意的还是UINavigationController
的错误。
我真正需要的是能够在UINavigationController
子类或其UINavigationControllerDelegate
看到何时取消交互式popup窗口。 有没有一个明显的方法来做到这一点?
编辑
我仍然在寻找一个解决scheme,但要提一下,我已经把这个问题报告为苹果的一个bug。 查看文档,这些委托方法不应该被调用,特别是考虑到调用等效的视图生命周期方法。
EDIT2
我的雷达票(16823313)今天(2015年5月21日)closures,并按预期标记。 🙁
工程部门根据以下信息确定此问题的行为如预期的那样:
这实际上是正确的行为。 从B – > A发生的导航转换,如果在转换过程中取消它,则不会得到didShowViewController:方法。 这个过渡的取消不应该被认为是从A→B的过渡,因为你从来没有真正达到A.
查看[Will / Did]出现仍然应该如期调用。
这是一个令人失望的情况,因为这是违反直觉的,但是我的回答中的解决方法在可预见的未来应该可以正常工作,至less对我来说是这样。
对于任何感兴趣的人,我已经find了2种方法在UINavigationControllerDelegate
级别解决这个问题。
-
使用KVO观察
interactivePopGestureRecognizer
的state
属性。 不幸的是,取消转换并不会将状态更改为UIGestureRecognizerStateFailed
,而只是UIGestureRecognizerStateEnded
,因此如果需要识别已取消或已完成的stream行音乐,则需要编写一些附加代码以跟踪发生的情况。 -
在testing之后,这可能是更好的解决scheme:使用
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]; }); } } }]; }
起初,我对这个解决scheme犹豫不决,因为关于你是否可以设置其中的一个以上(因为在这种情况下,如果一个不知名的视图控制器也设置了自己的通知块,它可能会替代这个一个或被这个replace)。 经过testing,似乎它不是1:1的关系,你可以安全地添加多个通知块。
编辑
我编辑了上面的代码来延迟navigationController:didShowViewController:animated:
call只在应该完成animation以更接近预期的默认行为时被调用。
Swift 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) } } }
我翻译了@Dima对我的项目的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:)
,尽pipe我相信这是在Swift编译时检查的,而且如果尝试在未实现时调用它。