UINavigationController“pushViewController:animated”的完成处理程序?
我正在使用UINavigationController
创build一个应用程序来呈现下一个视图控制器。 用iOS5有一个新的方法来呈现UIViewControllers
:
presentViewController:animated:completion:
现在我问我为什么没有UINavigationController
的完成处理程序? 有刚刚
pushViewController:animated:
是否有可能创build我自己的完成处理程序,如新的presentViewController:animated:completion:
请参阅帕尔的答案为另一个和更多最新的解决scheme
UINavigationController
animation是用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