ios UINavigationController“pushViewController:animated”的完成处理程序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9906966/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Completion handler for UINavigationController "pushViewController:animated"?
提问by geforce
I'm about creating an app using a UINavigationController
to present the next view controllers.
With iOS5 there′s a new method to presenting UIViewControllers
:
我将使用 a 创建一个应用程序UINavigationController
来呈现下一个视图控制器。在 iOS5 中有一种新的呈现方法UIViewControllers
:
presentViewController:animated:completion:
Now I ask me why isn′t there a completion handler for UINavigationController
?
There are just
现在我问我为什么没有完成处理程序UINavigationController
?有只是
pushViewController:animated:
Is it possible to create my own completion handler like the new presentViewController:animated:completion:
?
是否可以像 new 一样创建我自己的完成处理程序presentViewController:animated:completion:
?
回答by chrs
See par's answerfor another and more up to date solution
请参阅par 的答案以获取另一个更新的解决方案
UINavigationController
animations are run with CoreAnimation
, so it would make sense to encapsulate the code within CATransaction
and thus set a completion block.
UINavigationController
动画使用 运行CoreAnimation
,因此将代码封装在其中CATransaction
并设置完成块是有意义的。
Swift:
斯威夫特:
For swift I suggest creating an extension as such
对于 swift 我建议创建一个扩展
extension UINavigationController {
public func pushViewController(viewController: UIViewController,
animated: Bool,
completion: @escaping (() -> Void)?) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
pushViewController(viewController, animated: animated)
CATransaction.commit()
}
}
Usage:
用法:
navigationController?.pushViewController(vc, animated: true) {
// Animation done
}
Objective-C
目标-C
Header:
标题:
#import <UIKit/UIKit.h>
@interface UINavigationController (CompletionHandler)
- (void)completionhandler_pushViewController:(UIViewController *)viewController
animated:(BOOL)animated
completion:(void (^)(void))completion;
@end
Implementation:
执行:
#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
回答by par
iOS 7+ Swift
iOS 7+ 斯威夫特
Swift 4:
斯威夫特 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 版本。在这个版本中,我删除了 Swift 2 版本中显示的示例协同动画,因为它似乎让很多人感到困惑。
Swift 3:
斯威夫特 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:
斯威夫特 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()
}
)
}
}
回答by Daniel
Based on par's answer(which was the only one that worked with iOS9), but simpler and with a missing else (which could have led to the completion never being called):
基于par 的答案(这是唯一一个适用于 iOS9 的答案),但更简单并且缺少 else(这可能导致从未调用完成):
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()
}
}
}
回答by Klaas
Currently the UINavigationController
does not support this. But there's the UINavigationControllerDelegate
that you can use.
目前UINavigationController
不支持这个。但是有UINavigationControllerDelegate
你可以使用的。
An easy way to accomplish this is by subclassing UINavigationController
and adding a completion block property:
实现此目的的一种简单方法是通过子类化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
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:
这个新的子类可以在 Interface Builder 中分配,也可以像这样以编程方式使用:
PbNavigationController *nc = [[PbNavigationController alloc]initWithRootViewController:yourRootViewController];
回答by Francois Nadeau
Here is the Swift 4 version with the Pop.
这是带有 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()
}
}
Just in case someone else needs this.
以防万一其他人需要这个。
回答by Sam
To expand on @Klaas' answer (and as a result of thisquestion) I've added completion blocks directly to the push method:
为了扩展@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
To be used as follows:
使用方法如下:
UIViewController *vc = ...;
[(PbNavigationController *)self.navigationController pushViewController:vc animated:YES completion:^ {
NSLog(@"COMPLETED");
}];
回答by wj2061
Since iOS 7.0,you can use UIViewControllerTransitionCoordinator
to add a push completion block:
从 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");
}];
回答by rahul_send89
Swift 2.0
斯威夫特 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
}
}
}
回答by nzeltzer
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:
这是一个维护委托功能的文档化实现: