Completion handler for UINavigationController “pushViewController:animated”?

前端 未结 9 1007
礼貌的吻别
礼貌的吻别 2020-11-30 18:08

I\'m about creating an app using a UINavigationController to present the next view controllers. With iOS5 there´s a new method to presenting UIViewControl

相关标签:
9条回答
  • 2020-11-30 18:26

    iOS 7+ Swift

    Swift 4:

    // 2018.10.30 par:
    //   I've updated this answer with an asynchronous dispatch to the main queue
    //   when we're called without animation. This really should have been in the
    //   previous solutions I gave but I forgot to add it.
    extension UINavigationController {
        public func pushViewController(
            _ viewController: UIViewController,
            animated: Bool,
            completion: @escaping () -> Void)
        {
            pushViewController(viewController, animated: animated)
    
            guard animated, let coordinator = transitionCoordinator else {
                DispatchQueue.main.async { completion() }
                return
            }
    
            coordinator.animate(alongsideTransition: nil) { _ in completion() }
        }
    
        func popViewController(
            animated: Bool,
            completion: @escaping () -> Void)
        {
            popViewController(animated: animated)
    
            guard animated, let coordinator = transitionCoordinator else {
                DispatchQueue.main.async { completion() }
                return
            }
    
            coordinator.animate(alongsideTransition: nil) { _ in completion() }
        }
    }
    

    EDIT: I've added a Swift 3 version of my original answer. In this version I've removed the example co-animation shown in the Swift 2 version as it seems to have confused a lot of people.

    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, e.g.:
                { context in
                    viewController.setNeedsStatusBarAppearanceUpdate()
                },
                completion: { context in
                    completion()
                }
            )
        }
    }
    
    0 讨论(0)
  • 2020-11-30 18:33

    It takes a little more pipework to add this behavior and retain the ability to set an external delegate.

    Here's a documented implementation that maintains delegate functionality:

    LBXCompletingNavigationController

    0 讨论(0)
  • 2020-11-30 18:36

    Currently the UINavigationController does not support this. But there's the UINavigationControllerDelegate that you can use.

    An easy way to accomplish this is by subclassing UINavigationController and adding a completion block property:

    @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
    

    Before pushing the new view controller you would have to set the completion block:

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

    This new subclass can either be assigned in Interface Builder or be used programmatically like this:

    PbNavigationController *nc = [[PbNavigationController alloc]initWithRootViewController:yourRootViewController];
    
    0 讨论(0)
提交回复
热议问题