ios popViewController 的完成块
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12904410/
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 block for popViewController
提问by Ben Packard
When dismissing a modal view controller using dismissViewController
, there is the option to provide a completion block. Is there a similar equivalent for popViewController
?
使用 解除模态视图控制器时dismissViewController
,可以选择提供完成块。有类似的等价物popViewController
吗?
The completion argument is quite handy. For instance, I can use it to hold off removing a row from a tableview until the modal is off screen, letting the user see the row animation. When returning from a pushed view controller, I would like the same opportunity.
完成参数非常方便。例如,我可以使用它来推迟从 tableview 中删除一行,直到模态离开屏幕,让用户看到行动画。从推送的视图控制器返回时,我希望有同样的机会。
I have tried placing popViewController
in an UIView
animation block, where I do have access to a completion block. However, this produces some unwanted side effects on the view being popped to.
我尝试放置popViewController
在一个UIView
动画块中,在那里我可以访问一个完成块。但是,这会对弹出的视图产生一些不需要的副作用。
If there is no such method available, what are some workarounds?
如果没有这样的方法可用,有哪些解决方法?
回答by Joris Kluivers
I know an answer has been accepted over two years ago, however this answer is incomplete.
我知道两年前已经接受了一个答案,但是这个答案是不完整的。
There is no way to do what you're wanting out-of-the-box
没有办法做你想做的开箱即用的
This is technically correct because the UINavigationController
API doesn't offer any options for this. However by using the CoreAnimation framework it's possible to add a completion block to the underlying animation:
这在技术上是正确的,因为UINavigationController
API 没有为此提供任何选项。然而,通过使用 CoreAnimation 框架,可以向底层动画添加一个完成块:
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// handle completion here
}];
[self.navigationController popViewControllerAnimated:YES];
[CATransaction commit];
The completion block will be called as soon as the animation used by popViewControllerAnimated:
ends. This functionality has been available since iOS 4.
一旦popViewControllerAnimated:
结束使用的动画,将立即调用完成块。此功能从 iOS 4 开始可用。
回答by HotJard
For iOS9SWIFT version - works like a charm (hadn't tested for earlier versions). Based on this answer
对于iOS9SWIFT 版本 - 就像一个魅力(尚未针对早期版本进行测试)。基于这个答案
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()
}
}
}
回答by Arbitur
I made a Swift
version with extensions with @JorisKluiversanswer.
我制作了一个Swift
带有@JorisKluivers答案的扩展版本。
This will call a completion closure after the animation is done for both push
and pop
.
这将在为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()
}
}
回答by Jos Jong
I had the same issue. And because I had to use it in multiple occasions, and within chains of completion blocks, I created this generic solution in an UINavigationController subclass:
我遇到过同样的问题。因为我不得不在多个场合使用它,并且在完成块链中,我在 UINavigationController 子类中创建了这个通用解决方案:
- (void) navigationController:(UINavigationController *) navigationController didShowViewController:(UIViewController *) viewController animated:(BOOL) animated {
if (_completion) {
dispatch_async(dispatch_get_main_queue(),
^{
_completion();
_completion = nil;
});
}
}
- (UIViewController *) popViewControllerAnimated:(BOOL) animated completion:(void (^)()) completion {
_completion = completion;
return [super popViewControllerAnimated:animated];
}
Assuming
假设
@interface NavigationController : UINavigationController <UINavigationControllerDelegate>
and
和
@implementation NavigationController {
void (^_completion)();
}
and
和
- (id) initWithRootViewController:(UIViewController *) rootViewController {
self = [super initWithRootViewController:rootViewController];
if (self) {
self.delegate = self;
}
return self;
}
回答by mattjgalloway
There is no way to do what you're wanting out-of-the-box. i.e. there is no method with a completion block for popping a view controller from a nav stack.
没有办法立即执行您想要的操作。即没有用于从导航堆栈弹出视图控制器的完成块的方法。
What I would do is put the logic in viewDidAppear
. That will be called when the view has finished coming on screen. It'll be called for all different scenarios of the view controller appearing, but that should be fine.
我要做的是将逻辑放入viewDidAppear
. 当视图完成出现在屏幕上时,将调用它。它将为出现的视图控制器的所有不同场景调用,但这应该没问题。
Or you could use the UINavigationControllerDelegate
method navigationController:didShowViewController:animated:
to do a similar thing. This is called when the navigation controller has finished pushing or popping a view controller.
或者您可以使用该UINavigationControllerDelegate
方法navigationController:didShowViewController:animated:
来做类似的事情。当导航控制器完成推送或弹出视图控制器时调用它。
回答by Muhammad Waqas
SWIFT 4.1
斯威夫特 4.1
extension UINavigationController {
func pushToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.pushViewController(viewController, animated: animated)
CATransaction.commit()
}
func popViewController(animated:Bool = true, completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.popViewController(animated: animated)
CATransaction.commit()
}
func popToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.popToViewController(viewController, animated: animated)
CATransaction.commit()
}
func popToRootViewController(animated:Bool = true, completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.popToRootViewController(animated: animated)
CATransaction.commit()
}
}
回答by rshev
Working with or without animation properly, and also includes popToRootViewController
:
正确使用或不使用动画,还包括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)
}
}
回答by Vitalii
Based on @HotJard's answer, when all you want is just a couple of lines of code. Quick and Easy.
根据@HotJard 的回答,当您只需要几行代码时。快捷方便。
Swift 4:
斯威夫特 4:
_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
doWhatIWantAfterContollerHasPopped()
}
回答by Fattie
For 2018 ...
对于 2018 年...
if you have this ...
如果你有这个...
navigationController?.popViewController(animated: false)
// I want this to happen next, help! ->
nextStep()
and you want to add a completion ...
并且您想添加完成...
CATransaction.begin()
navigationController?.popViewController(animated: true)
CATransaction.setCompletionBlock({ [weak self] in
self?.nextStep() })
CATransaction.commit()
it's that simple.
就这么简单。
Handy tip...
贴心提示...
It's the same deal for the handy popToViewController
call.
方便的popToViewController
通话也是如此。
A typical thing is you have an onboarding stack of a zillion screens. When finally done, you go all the way back to your "base" screen, and then finally fire up the app.
一个典型的事情是你有无数个屏幕的入职堆栈。最终完成后,您会一直回到“基本”屏幕,然后最终启动应用程序。
So in the "base" screen, to go "all the way back", popToViewController(self
所以在“基地”屏幕,“一路回来”, popToViewController(self
func onboardingStackFinallyComplete() {
CATransaction.begin()
navigationController?.popToViewController(self, animated: false)
CATransaction.setCompletionBlock({ [weak self] in
guard let self = self else { return }
.. actually launch the main part of the app
})
CATransaction.commit()
}
回答by Benobab
Swift 3 answer, thanks to this answer: https://stackoverflow.com/a/28232570/3412567
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()
}
}