UINavigationController“pushViewController:animated”的完成处理程序?

我正在使用UINavigationController创build一个应用程序来呈现下一个视图控制器。 用iOS5有一个新的方法来呈现UIViewControllers

 presentViewController:animated:completion: 

现在我问我为什么没有UINavigationController的完成处理程序? 有刚刚

 pushViewController:animated: 

是否有可能创build我自己的完成处理程序,如新的presentViewController:animated:completion:

请参阅帕尔的答案为另一个和更多最新的解决scheme

UINavigationControlleranimation是用CoreAnimation运行的,所以将代码封装在CATransaction ,因此设置一个完成块。

Swift

为了迅速,我build议创build一个扩展

 extension UINavigationController { public func pushViewController(viewController: UIViewController, animated: Bool, completion: @escaping (() -> Void)?) { CATransaction.begin() CATransaction.setCompletionBlock(completion) pushViewController(viewController, animated: animated) CATransaction.commit() } } 

用法:

 navigationController?.pushViewController(vc, animated: true) { // Animation done } 

Objective-C的

标题:

 #import <UIKit/UIKit.h> @interface UINavigationController (CompletionHandler) - (void)completionhandler_pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion; @end 

执行:

 #import "UINavigationController+CompletionHandler.h" #import <QuartzCore/QuartzCore.h> @implementation UINavigationController (CompletionHandler) - (void)completionhandler_pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion { [CATransaction begin]; [CATransaction setCompletionBlock:completion]; [self pushViewController:viewController animated:animated]; [CATransaction commit]; } @end 

iOS 7 + Swift

编辑:我已经添加了我的原始答案的Swift 3版本。 在这个版本中,我删除了Swift 2版本中显示的示例协同animation,因为它似乎让很多人感到困惑。

Swift 3:

 import UIKit // Swift 3 version, no co-animation (alongsideTransition parameter is nil) extension UINavigationController { public func pushViewController( _ viewController: UIViewController, animated: Bool, completion: @escaping (Void) -> Void) { pushViewController(viewController, animated: animated) guard animated, let coordinator = transitionCoordinator else { completion() return } coordinator.animate(alongsideTransition: nil) { _ in completion() } } } 

Swift 2:

 import UIKit // Swift 2 Version, shows example co-animation (status bar update) extension UINavigationController { public func pushViewController( viewController: UIViewController, animated: Bool, completion: Void -> Void) { pushViewController(viewController, animated: animated) guard animated, let coordinator = transitionCoordinator() else { completion() return } coordinator.animateAlongsideTransition( // pass nil here or do something animated if you'd like, eg: { context in viewController.setNeedsStatusBarAppearanceUpdate() }, completion: { context in completion() } ) } } 

目前UINavigationController不支持这个。 但是可以使用UINavigationControllerDelegate

一个简单的方法来完成这个是通过UINavigationController并添加完成块属性:

 @interface PbNavigationController : UINavigationController <UINavigationControllerDelegate> @property (nonatomic,copy) dispatch_block_t completionBlock; @end @implementation PbNavigationController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { self.delegate = self; } return self; } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { NSLog(@"didShowViewController:%@", viewController); if (self.completionBlock) { self.completionBlock(); self.completionBlock = nil; } } @end 

推动新的视图控制器之前,你必须设置完成块:

 UIViewController *vc = ...; ((PbNavigationController *)self.navigationController).completionBlock = ^ { NSLog(@"COMPLETED"); }; [self.navigationController pushViewController:vc animated:YES]; 

这个新的子类可以在Interface Builder中分配,也可以像这样以编程方式使用:

 PbNavigationController *nc = [[PbNavigationController alloc]initWithRootViewController:yourRootViewController]; 

为了扩大@Klaas的答案(和作为这个问题的结果)我已经完成块直接添加到推的方法:

 @interface PbNavigationController : UINavigationController <UINavigationControllerDelegate> @property (nonatomic,copy) dispatch_block_t completionBlock; @property (nonatomic,strong) UIViewController * pushedVC; @end @implementation PbNavigationController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { self.delegate = self; } return self; } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { NSLog(@"didShowViewController:%@", viewController); if (self.completionBlock && self.pushedVC == viewController) { self.completionBlock(); } self.completionBlock = nil; self.pushedVC = nil; } -(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if (self.pushedVC != viewController) { self.pushedVC = nil; self.completionBlock = nil; } } -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(dispatch_block_t)completion { self.pushedVC = viewController; self.completionBlock = completion; [self pushViewController:viewController animated:animated]; } @end 

使用方法如下:

 UIViewController *vc = ...; [(PbNavigationController *)self.navigationController pushViewController:vc animated:YES completion:^ { NSLog(@"COMPLETED"); }]; 

根据par的答案 (这是唯一一个与iOS9一起工作的答案),但更简单,并有一个其他的失踪(这可能导致完成从未被称为):

 extension UINavigationController { func pushViewController(_ viewController: UIViewController, animated: Bool, completion: @escaping () -> Void) { pushViewController(viewController, animated: animated) if animated, let coordinator = transitionCoordinator { coordinator.animate(alongsideTransition: nil) { _ in completion() } } else { completion() } } func popViewController(animated: Bool, completion: @escaping () -> Void) { popViewController(animated: animated) if animated, let coordinator = transitionCoordinator { coordinator.animate(alongsideTransition: nil) { _ in completion() } } else { completion() } } } 

从iOS 7.0开始,您可以使用UIViewControllerTransitionCoordinator添加一个推送完成块:

 UINavigationController *nav = self.navigationController; [nav pushViewController:vc animated:YES]; id<UIViewControllerTransitionCoordinator> coordinator = vc.transitionCoordinator; [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { NSLog(@"push completed"); }]; 

Swift 2.0

 extension UINavigationController : UINavigationControllerDelegate { private struct AssociatedKeys { static var currentCompletioObjectHandle = "currentCompletioObjectHandle" } typealias Completion = @convention(block) (UIViewController)->() var completionBlock:Completion?{ get{ let chBlock = unsafeBitCast(objc_getAssociatedObject(self, &AssociatedKeys.currentCompletioObjectHandle), Completion.self) return chBlock as Completion }set{ if let newValue = newValue { let newValueObj : AnyObject = unsafeBitCast(newValue, AnyObject.self) objc_setAssociatedObject(self, &AssociatedKeys.currentCompletioObjectHandle, newValueObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } func popToViewController(animated: Bool,comp:Completion){ if (self.delegate == nil){ self.delegate = self } completionBlock = comp self.popViewControllerAnimated(true) } func pushViewController(viewController: UIViewController, comp:Completion) { if (self.delegate == nil){ self.delegate = self } completionBlock = comp self.pushViewController(viewController, animated: true) } public func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool){ if let comp = completionBlock{ comp(viewController) completionBlock = nil self.delegate = nil } } } 

它需要更多的pipe道来添加此行为,并保留设置外部委托的能力。

这是一个logging的实现,它维护委托function:

LBXCompletingNavigationController