popViewController的完成块
当使用dismissViewController
解除模态视图控制器时,可以select提供一个完成块。 有没有类似的popViewController
?
完成论证是非常方便的。 例如,我可以使用它阻止从tableview中删除一行,直到模式被closures,让用户看到行animation。 从推视图控制器返回时,我想同样的机会。
我已经尝试将popViewController
放置在一个UIView
animation块中,我可以访问完成块。 但是,这会对正在popup的视图产生一些不需要的副作用。
如果没有这样的方法,有什么解决方法?
我知道两年前已经接受了一个答案,但是这个答案是不完整的。
没有办法做你想要的东西
这在技术上是正确的,因为UINavigationController
API不提供任何选项。 然而,通过使用CoreAnimation框架,可以向底层animation添加一个完成块:
[CATransaction begin]; [CATransaction setCompletionBlock:^{ // handle completion here }]; [self.navigationController popViewControllerAnimated:YES]; [CATransaction commit];
一旦popViewControllerAnimated:
使用的animation结束,就会调用完成块。 此function自iOS 4起已可用。
对于iOS9的 SWIFT版本 – 像一个魅力(没有testing过早期版本)。 根据这个答案
extension UINavigationController { func pushViewController(viewController: UIViewController, animated: Bool, completion: () -> ()) { pushViewController(viewController, animated: animated) if let coordinator = transitionCoordinator() where animated { coordinator.animateAlongsideTransition(nil) { _ in completion() } } else { completion() } } func popViewController(animated: Bool, completion: () -> ()) { popViewControllerAnimated(animated) if let coordinator = transitionCoordinator() where animated { coordinator.animateAlongsideTransition(nil) { _ in completion() } } else { completion() } } }
我使用@JorisKluivers答案做了一个扩展的Swift
版本。
在push
和pop
animation完成之后,这将调用完成closures。
extension UINavigationController { func popViewControllerWithHandler(completion: ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.popViewControllerAnimated(true) CATransaction.commit() } func pushViewController(viewController: UIViewController, completion: ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.pushViewController(viewController, animated: true) CATransaction.commit() } }
我遇到过同样的问题。 因为我不得不在多个场合使用它,并且在完成块的链中,我在UINavigationController子类中创build了这个通用解决scheme:
- (void) navigationController:(UINavigationController *) navigationController didShowViewController:(UIViewController *) viewController animated:(BOOL) animated { if (_completion) { _completion(); _completion = nil; } } - (UIViewController *) popViewControllerAnimated:(BOOL) animated completion:(void (^)()) completion { _completion = completion; return [super popViewControllerAnimated:animated]; }
假设
@interface NavigationController : UINavigationController <UINavigationControllerDelegate>
和
@implementation NavigationController { void (^_completion)(); }
和
- (id) initWithRootViewController:(UIViewController *) rootViewController { self = [super initWithRootViewController:rootViewController]; if (self) { self.delegate = self; } return self; }
没有办法做你想要的东西。 即没有一个方法具有用于从导航堆栈中popup视图控制器的完成块。
我会做的是把viewDidAppear
的逻辑。 当视图在屏幕上显示时会被调用。 这将被称为所有不同的视图控制器的情况出现,但是应该没问题。
或者你可以使用UINavigationControllerDelegate
方法navigationController:didShowViewController:animated:
做类似的事情。 这在导航控制器完成推送或popup视图控制器时调用。
正确使用或不使用animation,还包括popToRootViewController
:
// updated for Swift 3.0 extension UINavigationController { private func doAfterAnimatingTransition(animated: Bool, completion: @escaping (() -> Void)) { if let coordinator = transitionCoordinator, animated { coordinator.animate(alongsideTransition: nil, completion: { _ in completion() }) } else { DispatchQueue.main.async { completion() } } } func pushViewController(viewController: UIViewController, animated: Bool, completion: @escaping (() -> Void)) { pushViewController(viewController, animated: animated) doAfterAnimatingTransition(animated: animated, completion: completion) } func popViewController(animated: Bool, completion: @escaping (() -> Void)) { popViewController(animated: animated) doAfterAnimatingTransition(animated: animated, completion: completion) } func popToRootViewController(animated: Bool, completion: @escaping (() -> Void)) { popToRootViewController(animated: animated) doAfterAnimatingTransition(animated: animated, completion: completion) } }
Swift 3答案,感谢这个答案: https : //stackoverflow.com/a/28232570/3412567
//MARK:UINavigationController Extension extension UINavigationController { //Same function as "popViewController", but allow us to know when this function ends func popViewControllerWithHandler(completion: @escaping ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.popViewController(animated: true) CATransaction.commit() } func pushViewController(viewController: UIViewController, completion: @escaping ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.pushViewController(viewController, animated: true) CATransaction.commit() } }
在所呈现的视图控制器上调用viewDidDisappear方法之后调用完成块。因此,将代码放入popup视图控制器的viewDidDisappear方法中应该与完成块相同。
我用块精确地实现了这一点。 我希望获取的结果控制器显示模态视图添加的行,只有在完全离开屏幕后,用户才能看到发生的变化。 在准备负责显示模态视图控制器的segue时,我设置了当模态消失时要执行的模块。 而在模式视图控制器我重写viewDidDissapear,然后调用该块。 我只是简单地开始更新,当模式将出现,并在消失时结束更新,但那是因为我使用NSFetchedResultsController,但是你可以做任何你喜欢的块内。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"addPassword"]){ UINavigationController* nav = (UINavigationController*)segue.destinationViewController; AddPasswordViewController* v = (AddPasswordViewController*)nav.topViewController; ... // makes row appear after modal is away. [self.tableView beginUpdates]; [v setViewDidDissapear:^(BOOL animated) { [self.tableView endUpdates]; }]; } } @interface AddPasswordViewController : UITableViewController<UITextFieldDelegate> ... @property (nonatomic, copy, nullable) void (^viewDidDissapear)(BOOL animated); @end @implementation AddPasswordViewController{ ... -(void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; if(self.viewDidDissapear){ self.viewDidDissapear(animated); } } @end
有一个名为UINavigationControllerWithCompletionBlock的pod,当在UINavigationController上推送和popup时,添加对完成块的支持。
为了完整起见,我已经准备好了一个Objective-C类:
// UINavigationController+CompletionBlock.h #import <UIKit/UIKit.h> @interface UINavigationController (CompletionBlock) - (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion; @end
// UINavigationController+CompletionBlock.m #import "UINavigationController+CompletionBlock.h" @implementation UINavigationController (CompletionBlock) - (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion { [CATransaction begin]; [CATransaction setCompletionBlock:^{ completion(); }]; UIViewController *vc = [self popViewControllerAnimated:animated]; [CATransaction commit]; return vc; } @end