当使用dismissViewController
解雇模态视图控制器时,可以选择提供完成块。 popViewController
是否有类似的等价物?
完成论证非常方便。例如,我可以使用它来阻止从tableview中删除一行,直到模态关闭屏幕,让用户看到行动画。当从推出的视图控制器返回时,我希望有同样的机会。
我尝试将popViewController
放在UIView
动画块中,我可以访问完成块。但是,这会对弹出的视图产生一些不必要的副作用。
如果没有这样的方法,有什么办法吗?
答案 0 :(得分:177)
我知道两年前已经接受了答案,但这个答案是不完整的。
没有办法做你想要开箱即用的事情
这在技术上是正确的,因为UINavigationController
API并未为此提供任何选项。但是,通过使用CoreAnimation框架,可以将完成块添加到基础动画中:
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// handle completion here
}];
[self.navigationController popViewControllerAnimated:YES];
[CATransaction commit];
popViewControllerAnimated:
使用的动画结束后,将立即调用完成块。此功能自iOS 4开始提供。
答案 1 :(得分:45)
对于 iOS9 SWIFT版本 - 就像一个魅力(未经过早期版本测试)。基于this answer
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()
}
}
}
答案 2 :(得分:27)
我制作了Swift
版本,附带 @JorisKluivers 答案。
这将在push
和pop
动画完成后调用完成闭包。
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()
}
}
答案 3 :(得分:16)
我有同样的问题。因为我必须在多个场合使用它,并且在完成块的链中,我在UINavigationController子类中创建了这个通用解决方案:
- (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;
}
答案 4 :(得分:15)
没有办法做你想要开箱即用的东西。即,没有方法可以使用完成块从导航堆栈中弹出视图控制器。
我要做的是将逻辑放在viewDidAppear
中。当视图完成屏幕时,将调用该方法。它将被视为出现视图控制器的所有不同场景,但这应该没问题。
或者你可以使用UINavigationControllerDelegate
方法navigationController:didShowViewController:animated:
来做类似的事情。当导航控制器完成推动或弹出视图控制器时调用此方法。
答案 5 :(得分:10)
正确使用或不使用动画,还包括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)
}
}
答案 6 :(得分:7)
基于@ HotJard的答案,当你想要的只是几行代码时。快速而简单。
Swift 4 :
_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
doWantIWantAfterContollerHasPopped()
}
答案 7 :(得分:6)
SWIFT 4.1
$token = (Invoke-WebRequest -Uri "https://stage-ansible-tower.hc.cloud.uk.hsbc/api/v1/authtoken/" -Body $json -ContentType "application/json" -Method POST).content | ConvertFrom-Json
答案 8 :(得分:5)
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()
}
}
答案 9 :(得分:4)
在显示的视图控制器上调用viewDidDisappear方法后调用完成块,因此将代码放在弹出视图控制器的viewDidDisappear方法中应该与完成块相同。
答案 10 :(得分:3)
如果你有这个......
navigationController?.popViewController(animated: false)
nextStep()
并且您想要添加完成...
CATransaction.begin()
navigationController?.popViewController(animated: true)
CATransaction.setCompletionBlock({ [weak self] in
self?.nextStep() })
CATransaction.commit()
就这么简单。
答案 11 :(得分:2)
有一个名为UINavigationControllerWithCompletionBlock的pod,它在UINavigationController上推送和弹出时都会添加对完成块的支持。
答案 12 :(得分:2)
2020 Swift 5.1方式
此解决方案保证在popViewController完全完成之后执行完成。您可以通过在NavigationController上完成其他操作来对其进行测试:在以上所有其他解决方案中,UINavigationController仍在忙于popViewController操作,并且没有响应。
public class NavigationController: UINavigationController, UINavigationControllerDelegate
{
private var completion: (() -> Void)?
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool)
{
if self.completion != nil {
DispatchQueue.main.async(execute: {
self.completion?()
self.completion = nil
})
}
}
func popViewController(animated: Bool, completion: @escaping () -> Void) -> UIViewController?
{
self.completion = completion
return super.popViewController(animated: animated)
}
}
答案 13 :(得分:2)
请参考最新版本(5.1) Swifty & SDK-like方式,
extension UINavigationController {
func popViewController(animated: Bool, completion: (() -> ())? = nil) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
popViewController(animated: animated)
CATransaction.commit()
}
func pushViewController(_ viewController: UIViewController, animated: Bool, completion: (() -> ())? = nil) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
pushViewController(viewController, animated: animated)
CATransaction.commit()
}
}
答案 14 :(得分:1)
在代码中使用下一个扩展名:(Swift 4)
import UIKit
extension UINavigationController {
func popViewController(animated: Bool = true, completion: @escaping () -> Void) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
popViewController(animated: animated)
CATransaction.commit()
}
func pushViewController(_ viewController: UIViewController, animated: Bool = true, completion: @escaping () -> Void) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
pushViewController(viewController, animated: animated)
CATransaction.commit()
}
}
答案 15 :(得分:1)
我使用块精确地实现了这一点。我希望我的获取结果控制器显示模态视图添加的行,只有在完全离开屏幕后才会显示,因此用户可以看到发生的更改。在准备负责显示模态视图控制器的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
答案 16 :(得分:1)
带有可选viewController参数的Swift 4版本,可以弹出一个特定的参数。
extension UINavigationController {
func pushViewController(viewController: UIViewController, animated:
Bool, completion: @escaping () -> ()) {
pushViewController(viewController, animated: animated)
if let coordinator = transitionCoordinator, animated {
coordinator.animate(alongsideTransition: nil) { _ in
completion()
}
} else {
completion()
}
}
func popViewController(viewController: UIViewController? = nil,
animated: Bool, completion: @escaping () -> ()) {
if let viewController = viewController {
popToViewController(viewController, animated: animated)
} else {
popViewController(animated: animated)
}
if let coordinator = transitionCoordinator, animated {
coordinator.animate(alongsideTransition: nil) { _ in
completion()
}
} else {
completion()
}
}
}
答案 17 :(得分:1)
根据this answer清理 Swift 4 版本。
extension UINavigationController {
func pushViewController(_ viewController: UIViewController, animated: Bool, completion: @escaping () -> Void) {
self.pushViewController(viewController, animated: animated)
self.callCompletion(animated: animated, completion: completion)
}
func popViewController(animated: Bool, completion: @escaping () -> Void) -> UIViewController? {
let viewController = self.popViewController(animated: animated)
self.callCompletion(animated: animated, completion: completion)
return viewController
}
private func callCompletion(animated: Bool, completion: @escaping () -> Void) {
if animated, let coordinator = self.transitionCoordinator {
coordinator.animate(alongsideTransition: nil) { _ in
completion()
}
} else {
completion()
}
}
}
答案 18 :(得分:1)
为了完整起见,我已经准备好使用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
答案 19 :(得分:0)
我认为 viewDidDisappear(_ animation: Bool) 函数可以帮助解决这个问题。当视图完全消失时,它会被调用。
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
//do the stuff here
}