web-dev-qa-db-ja.com

アプリがバックグラウンドから戻ったときにviewWillAppearが呼び出されないのはなぜですか?

私はアプリを書いていますが、ユーザーが電話で話しているときにアプリを見ている場合は、ビューを変更する必要があります。

次のメソッドを実装しました。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"viewWillAppear:");
    _sv.frame = CGRectMake(0.0, 0.0, 320.0, self.view.bounds.size.height);
}

ただし、アプリがフォアグラウンドに戻ったときに呼び出されることはありません。

私は実装できることを知っています:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];

しかし、私はこれをしたくありません。むしろ、すべてのレイアウト情報をviewWillAppear:メソッドに入れて、考えられるすべてのシナリオを処理できるようにします。

ViewWillAppear:をapplicationWillEnterForeground:から呼び出そうとしましたが、その時点で現在のView Controllerを特定することはできません。

誰もこれに対処する適切な方法を知っていますか?明らかな解決策が欠けていると確信しています。

259
Philip Walton

メソッドviewWillAppearは、別のアプリから切り替えたときにフォアグラウンドに置かれているアプリケーションのコンテキストではなく、独自のアプリケーションで行われていることのコンテキストで取得する必要があります。

言い換えれば、誰かが別のアプリケーションを見たり、電話をかけたり、バックグラウンドで以前にあったアプリ、つまりアプリを離れたときにすでに表示されていたUIViewControllerに切り替えると、いわば「気にしない」懸念される限り、消えることはなく、表示されたままです。したがって、viewWillAppearは呼び出されません。

viewWillAppearを自分で呼び出すことはお勧めしません-破壊すべきではない特定の意味があります!同じ効果を得るためにできるリファクタリングは次のとおりです。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self doMyLayoutStuff:self];
}

- (void)doMyLayoutStuff:(id)sender {
    // stuff
}

次に、適切な通知からdoMyLayoutStuffをトリガーします。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doMyLayoutStuff:) name:UIApplicationDidChangeStatusBarFrameNotification object:self];

どちらが「現在の」UIViewControllerであるかをすぐに確認できる方法はありません。しかし、あなたはそれを回避する方法を見つけることができます、例えばUIViewControllerがいつ表示されるかを調べるためのUINavigationControllerのデリゲートメソッドがあります。このようなものを使用して、提示された最新のUIViewControllerを追跡できます。

更新

さまざまなビットに適切な自動サイズ変更マスクを使用してUIをレイアウトする場合、UIの「手動」レイアウトを処理する必要さえない場合があります。

190
occulus

迅速

短い答え

NotificationCenterではなくviewWillAppearオブザーバーを使用します。

override func viewDidLoad() {
    super.viewDidLoad()

    // set observer for UIApplication.willEnterForegroundNotification
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

}

// my selector that was defined above
@objc func willEnterForeground() {
    // do stuff
}

長答

アプリがバックグラウンドから戻ったときを確認するには、NotificationCenterではなくviewWillAppearオブザーバーを使用します。これは、どのイベントがいつ発生するかを示すサンプルプロジェクトです。 (これは このObjective-Cの答え の適応です。)

import UIKit
class ViewController: UIViewController {

    // MARK: - Overrides

    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")

        // add notification observers
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

    }

    override func viewWillAppear(_ animated: Bool) {
        print("view will appear")
    }

    override func viewDidAppear(_ animated: Bool) {
        print("view did appear")
    }

    // MARK: - Notification oberserver methods

    @objc func didBecomeActive() {
        print("did become active")
    }

    @objc func willEnterForeground() {
        print("will enter foreground")
    }

}

アプリを最初に起動したときの出力順序は次のとおりです。

view did load
view will appear
did become active
view did appear

ホームボタンを押してからアプリをフォアグラウンドに戻した後、出力順序は次のとおりです。

will enter foreground
did become active 

したがって、最初にviewWillAppearを使用しようとした場合は、おそらくUIApplication.willEnterForegroundNotificationが必要です。

注意

IOS 9以降では、オブザーバーを削除する必要はありません。 ドキュメント 状態:

アプリがiOS 9.0以降またはmacOS 10.11以降をターゲットにしている場合、deallocメソッドでオブザーバーの登録を解除する必要はありません。

166
Suragch

ViewControllerのviewDidLoad:メソッドで通知センターを使用してメソッドを呼び出し、そこからviewWillAppear:メソッドで行うべきことを実行します。 viewWillAppear:を直接呼び出すことは適切なオプションではありません。

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"view did load");

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(applicationIsActive:) 
        name:UIApplicationDidBecomeActiveNotification 
        object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(applicationEnteredForeground:) 
        name:UIApplicationWillEnterForegroundNotification
        object:nil];
}

- (void)applicationIsActive:(NSNotification *)notification {
    NSLog(@"Application Did Become Active");
}

- (void)applicationEnteredForeground:(NSNotification *)notification {
    NSLog(@"Application Entered Foreground");
}
138
Manju

私の意見では、iOS SDKで最も混乱を招くメソッドの1つであるviewWillAppear:animated:は、このような状況、つまりアプリケーションの切り替えでは決して呼び出されません。そのメソッドは、View Controllerのビューとアプリケーションのウィンドウとの関係に従ってのみ呼び出されます。つまり、メッセージはViewが画面ではなくアプリケーションのウィンドウに表示される場合にのみView Controllerに送信されます。

アプリケーションがバックグラウンドになると、明らかにアプリケーションウィンドウの一番上のビューがユーザーに表示されなくなります。ただし、アプリケーションウィンドウのパースペクティブでは、これらは依然として最上位のビューであるため、ウィンドウから消えませんでした。むしろ、アプリケーションウィンドウが消えたため、これらのビューは消えました。ウィンドウから)消えたため、それらは消えませんでした。

したがって、ユーザーがアプリケーションに戻ると、ウィンドウが再び表示されるため、明らかに画面に表示されるように見えます。しかし、ウィンドウの観点からは、それらはまったく消えていません。したがって、View ControllerはviewWillAppear:animatedメッセージを取得しません。

33
MHC

できるだけ簡単にするために、次のコードを参照してください。

- (void)viewDidLoad
{
   [self appWillEnterForeground]; //register For Application Will enterForeground
}


- (id)appWillEnterForeground{ //Application will enter foreground.

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(allFunctions)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
    return self;
}


-(void) allFunctions{ //call any functions that need to be run when application will enter foreground 
    NSLog(@"calling all functions...application just came back from foreground");


}
3
ConfusedDeer