dismissViewController
を使用してモーダルビューコントローラーを閉じる場合、完了ブロックを提供するオプションがあります。 popViewController
に同様の同等物はありますか?
補完引数は非常に便利です。たとえば、モーダルが画面から外れるまでTableViewから行を削除しないようにして、ユーザーに行のアニメーションを表示させることができます。プッシュされたView Controllerから戻るとき、同じ機会が欲しいです。
popViewController
アニメーションブロックにUIView
を配置しようとしましたが、完了ブロックにアクセスできます。ただし、これにより、ポップされるビューにいくつかの望ましくない副作用が生じます。
そのような方法が利用できない場合、いくつかの回避策は何ですか?
2年以上前に回答が受け入れられましたが、この回答は不完全です。
すぐに使用したいことをする方法はありません
UINavigationController
APIはこのオプションを提供しないため、これは技術的に正しいです。ただし、CoreAnimationフレームワークを使用することで、基礎となるアニメーションに完了ブロックを追加することができます。
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// handle completion here
}];
[self.navigationController popViewControllerAnimated:YES];
[CATransaction commit];
popViewControllerAnimated:
によって使用されるアニメーションが終了するとすぐに、完了ブロックが呼び出されます。この機能は、iOS 4以降で使用可能です。
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()
}
}
}
@ JorisKluivers answerを使用して、拡張子付きのSwift
バージョンを作成しました。
これは、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()
}
}
同じ問題がありました。そして、私はそれを複数の場面で、また完了ブロックのチェーン内で使用しなければならなかったので、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;
}
すぐに使用したいことを行う方法はありません。つまり、navスタックからView Controllerをポップするための完了ブロックを持つメソッドはありません。
私がやることは、ロジックをviewDidAppear
に入れることです。ビューが画面に表示されたときに呼び出されます。表示されるView Controllerのすべての異なるシナリオに対して呼び出されますが、それは問題ないはずです。
または、UINavigationControllerDelegate
メソッドnavigationController:didShowViewController:animated:
同様のことを行います。これは、Navigation ControllerがView Controllerのプッシュまたはポップを完了したときに呼び出されます。
アニメーションの有無にかかわらず適切に動作し、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)
}
}
@HotJardの答えに基づいて、必要なのはほんの数行のコードだけです。早くて簡単。
Swift 4:
_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
doWantIWantAfterContollerHasPopped()
}
Swift 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: true)
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()
}
}
この回答のおかげで、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()
}
}
これがあれば...
navigationController?.popViewController(animated: false)
NeXTSTEP()
補完を追加したい...
CATransaction.begin()
navigationController?.popViewController(animated: true)
CATransaction.setCompletionBlock({ [weak self] in
self?.NeXTSTEP() })
CATransaction.commit()
とても簡単です。
提示されたView ControllerでviewDidDisappearメソッドが呼び出された後に完了ブロックが呼び出されるため、ポップされたView ControllerのviewDidDisappearメソッドにコードを配置すると、完了ブロックと同じように動作します。
特定のものにポップするためのオプションの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()
}
}
}
INavigationControllerWithCompletionBlock と呼ばれるポッドがあります。これは、UINavigationControllerでプッシュとポップの両方を行うときに完了ブロックのサポートを追加します。
クリーンアップ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()
}
}
}
ブロックを使用して正確にこれを正確に達成しました。取得した結果コントローラーに、モーダルビューによって追加された行を、画面から完全に離れてから一度だけ表示して、ユーザーが変更を確認できるようにしたかったのです。モーダルビューコントローラーの表示を担当するセグエの準備では、モーダルが消えたときに実行するブロックを設定します。モーダルビューコントローラーでは、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
コードで次の拡張機能を使用します:(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()
}
}
完全を期すために、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