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

gef*_*rce 103 handler uinavigationcontroller pushviewcontroller ios

我是要创建一个使用a UINavigationController来呈现下一个视图控制器的应用程序.iOS5有一种新的呈现方法UIViewControllers:

presentViewController:animated:completion:
Run Code Online (Sandbox Code Playgroud)

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

pushViewController:animated:
Run Code Online (Sandbox Code Playgroud)

是否有可能像新的一样创建自己的完成处理程序presentViewController:animated:completion:

chr*_*hrs 129

请参阅par的答案,了解另一个更新的解决方案

UINavigationController运行动画CoreAnimation,因此将代码封装在内部CATransaction并因此设置完成块是有意义的.

斯威夫特:

对于swift,我建议创建一个扩展

extension UINavigationController {

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

}
Run Code Online (Sandbox Code Playgroud)

用法:

navigationController?.pushViewController(vc, animated: true) {
  // Animation done
}
Run Code Online (Sandbox Code Playgroud)

Objective-C的

标题:

#import <UIKit/UIKit.h>

@interface UINavigationController (CompletionHandler)

- (void)completionhandler_pushViewController:(UIViewController *)viewController
                                    animated:(BOOL)animated
                                  completion:(void (^)(void))completion;

@end
Run Code Online (Sandbox Code Playgroud)

执行:

#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
Run Code Online (Sandbox Code Playgroud)

  • 这似乎在iOS 9设备上不可靠.有关替代方案,请参阅下面的我或@ par的答案 (7认同)

par*_*par 83

iOS 7+ 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() }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

斯威夫特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() }
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特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()
            }
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个你可以做并行动画的例子(它上面的注释表明它是可选的).传递'nil`也是一件非常有效的事情. (2认同)
  • 在某些情况下不适合我。animated 的值为true,但transitionCoordinator 的值为nil。不知道为什么会发生这种情况。 (2认同)

Dan*_*iel 27

基于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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Kla*_*aas 24

目前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
Run Code Online (Sandbox Code Playgroud)

在推送新视图控制器之前,您必须设置完成块:

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

这个新子类可以在Interface Builder中分配,也可以通过编程方式使用,如下所示:

PbNavigationController *nc = [[PbNavigationController alloc]initWithRootViewController:yourRootViewController];
Run Code Online (Sandbox Code Playgroud)

  • 添加一个映射到视图控制器的完成块列表可能会使这个最有用,而一个新的方法,可能叫做`pushViewController:animated:completion:`会使它成为一个优雅的解决方案. (8认同)

Sam*_*Sam 7

为了扩展@Klaas的答案(并且由于这个问题),我已经将完成块直接添加到push方法:

@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
Run Code Online (Sandbox Code Playgroud)

使用如下:

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


wj2*_*061 5

从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");
}];
Run Code Online (Sandbox Code Playgroud)


Fra*_*eau 5

这是带有Pop的Swift 4版本.

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

    public func popViewController(animated: Bool,
                                  completion: (() -> Void)?) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        popViewController(animated: animated)
        CATransaction.commit()
    }
}
Run Code Online (Sandbox Code Playgroud)

以防其他人需要这个.

  • 如果您对此运行一个简单的测试,您会发现完成块在动画完成之前触发。所以这可能没有提供许多人正在寻找的东西。 (3认同)