viewWillDisappear:ビューコントローラーがポップされているか、サブビューコントローラーが表示されているかを確認します


134

この問題の適切な解決策を見つけるのに苦労しています。ビューコントローラーの-viewWillDisappear:メソッドでは、ビューコントローラーがナビゲーションコントローラーのスタックにプッシュされているためか、ポップされたためにビューコントローラーが消えているためかを判断する方法を見つける必要があります。

現時点では、次のようなフラグを設定してisShowingChildViewControllerいますが、かなり複雑になっています。私がそれを検出できると思う唯一の方法は-deallocメソッドです。

回答:


228

以下を使用できます。

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  NSArray *viewControllers = self.navigationController.viewControllers;
  if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
    // View is disappearing because a new view controller was pushed onto the stack
    NSLog(@"New view controller was pushed");
  } else if ([viewControllers indexOfObject:self] == NSNotFound) {
    // View is disappearing because it was popped from the stack
    NSLog(@"View controller was popped");
  }
}

もちろん、これは可能です。viewWillDisappearが呼び出されるまでに、UINavigationControllerのビューコントローラースタック(viewControllersプロパティを通じて公開)が更新されているためです。


2
パーフェクト!どうしてそんなことを考えなかったのかわからない!非表示メソッドが呼び出されるまで、スタックが変更されるとは思いませんでした!おかげで:-)
マイケルの滝

1
私は同じことを実行しようとしていますがviewWillAppear、ビューコントローラーがプッシュされているか、その上の何かがポップされているかによって、ViewControllers配列が明らかになっているようです。何か案は?
マイケルの滝

また、ビューコントローラーはアプリの存続期間を通じて永続的であるため、viewDidLoad一度しか呼び出されないため、アクションを実行できません。うーん、トリッキーです!
マイケルの滝

4
@Sbrocketの![viewControllers containsObject:self]代わりにあなたがしなかった理由はあります[viewControllers indexOfObject:self] == NSNotFoundか?スタイルの選択は?
ゼケル

24
この回答はiOS 5で廃止されました。-isMovingFromParentViewController以下の方法で、ビューが明示的にポップされているかどうかをテストできます。
grahamparks 2013年

136

私は最も簡単な方法だと思います:

 - (void)viewWillDisappear:(BOOL)animated
{
    if ([self isMovingFromParentViewController])
    {
        NSLog(@"View controller was popped");
    }
    else
    {
        NSLog(@"New view controller was pushed");
    }
    [super viewWillDisappear:animated];
}

迅速:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParent
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}

iOS 5ではこれが答えです。おそらくisBeingDismissed
d370urn3ur

4
iOS7の場合、[self.navigationController.viewControllers indexOfObject:self] == NSNotFoundをもう一度チェックする必要があります。これは、アプリのバックグラウンドでもこのテストに合格するが、ナビゲーションスタックからselfが削除されないためです。
エリックチェン

3
Appleは、これを行うための文書化された方法を提供しています-stackoverflow.com/a/33478133/385708
Shyam Bhat

viewWillDisappearを使用する際の問題は、ビューがすでに非表示になっているときに、コントローラーがスタックからポップされる可能性があることです。たとえば、別のビューコントローラーをスタックの一番上にプッシュし、中央のビューコントローラーでviewWillDisappearをバイパスしてpopToRootViewControllerAnimatedを呼び出すことができます。
John K

ナビゲーションスタックに2つのコントローラー(ルートvcとプッシュされた別のコントローラー)があるとします。3番目のビューがプッシュされているとき、ビューが消える2番目のビューでviewWillDisappearが呼び出されますよね?したがって、ルートビューコントローラーにポップすると(3番目と2番目にポップ)、viewWillDisappearが3番目、つまりスタックの最後のvcで呼び出されます。これは、ビューが上にあり、この時点で非表示になるため、2番目のビューはすでに非表示になっているためです。このため、このメソッドはviewControllerWillBePoppedではなくviewWillDisappearと呼ばれます。
RTasche 16

61

UIViewController.hのAppleのドキュメントから:

「これらの4つのメソッドをビューコントローラーの外観コールバックで使用して、子ビューコントローラーとして表示、非表示、または追加または削除されているかどうかを判断できます。たとえば、ビューコントローラーは、非表示または式([self isBeingDismissed] || [self isMovingFromParentViewController])をチェックして、viewWillDisappear:メソッドで自分自身に質問することでポップされます。

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);

- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

したがって、これを行うための唯一の文書化された方法は、次の方法です。

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
    }
}

Swift 3バージョン:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed || self.isMovingFromParentViewController { 
    }
}

18

ビューがポップされているかどうかを知りたいだけの場合self.navigationControllernilviewDidDisappearそれがコントローラーのスタックから削除されたときににあることがわかりました。したがって、これは単純な代替テストです。

(これは、他のあらゆる種類のゆがみを試した後に発見されました。ポップで通知されるビューコントローラーを登録するためのナビゲーションコントローラープロトコルがないことに驚いています。UINavigationControllerDelegate実際に実際の表示作業を行うため、使用できません。)


16

スウィフト4

override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent
        {
            //View Controller Popped
        }
        else
        {
            //New view controller pushed
        }
    }

6

Swiftの場合:

 override func viewWillDisappear(animated: Bool) {
    if let navigationController = self.navigationController {
        if !contains(navigationController.viewControllers as! Array<UIViewController>, self) {
        }
    }

    super.viewWillDisappear(animated)

}

必ず使用してください!代わりに
dfmuir

2

これに関するAppleのドキュメントは理解しにくいと思います。この拡張機能は、各ナビゲーションの状態を確認するのに役立ちます。

extension UIViewController {
    public func printTransitionStates() {
        print("isBeingPresented=\(isBeingPresented)")
        print("isBeingDismissed=\(isBeingDismissed)")
        print("isMovingToParentViewController=\(isMovingToParentViewController)")
        print("isMovingFromParentViewController=\(isMovingFromParentViewController)")
    }
}

1

この質問はかなり古いですが、偶然見たので、ベストプラクティスを投稿したいと思います(afaik)

あなたはただすることができます

if([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
 // view controller popped
}

1

これはiOS7に適用され、他のiOS7に適用されるかどうかはわかりません。私が知っていることからviewDidDisappear、ビューではすでにポップされています。つまり、クエリを実行self.navigationController.viewControllersするとを取得しnilます。したがって、それがnilかどうかを確認してください。

TL; DR

 - (void)viewDidDisappear:(BOOL)animated
 {
    [super viewDidDisappear:animated];
    if (self.navigationController.viewControllers == nil) {
        // It has been popped!
        NSLog(@"Popped and Gone");
    }
 }

1

セグエは、iOS 6以降でこの問題を処理する非常に効果的な方法です。Interface Builderで特定のセグエに識別子を指定している場合は、で確認できますprepareForSegue

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"LoginSegue"]) {
       NSLog(@"Push");
       // Do something specific here, or set a BOOL indicating
       // a push has occurred that will be checked later
    }
}

1

おかげで@ブライアンヘンリー、まだスウィフト5で動作します

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if let controllers = navigationController?.children{
            if controllers.count > 1, controllers[controllers.count - 2] == self{
                // View is disappearing because a new view controller was pushed onto the stack
                print("New view controller was pushed")
            }
            else if controllers.firstIndex(of: self) == nil{
                // View is disappearing because it was popped from the stack
                print("View controller was popped")
            }
        }

    }

-1

スタックにプッシュされたと言うときに、新しいビューをプッシュすることによって、ビューがナビゲーションコントローラーのスタックの下に移動されていることを想定しています。私が使用してことをお勧めviewDidUnload追加する方法をNSLog追加したいことがあり、あなたは何が起こっているか見ることができるように書き込みにコンソールに何かを声明をNSLogしますviewWillDissappeer


-1

これはsbrocketの答えと同じことを達成するためのカテゴリです:

ヘッダ:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

ソース:

#import "UIViewController+isBeingPopped.h"

@implementation UIViewController (isBeingPopped)

- (BOOL) isBeingPopped {
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
        return NO;
    } else if ([viewControllers indexOfObject:self] == NSNotFound) {
        return YES;
    }
    return NO;
}

@end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.