1. ホーム
  2. アイオス

[解決済み】アプリがバックグラウンドから戻ってきたときに、viewWillAppearが呼ばれないのはなぜですか?

2022-03-29 20:16:08

質問

アプリを書いているのですが、ユーザーが電話で話しながらアプリを見ている場合、表示を変更する必要があります。

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

- (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:メソッドにレイアウト情報をすべて入れて、すべての可能なシナリオを処理できるようにしたいのです。

applicationWillEnterForeground: から viewWillAppear: を呼び出そうとしたこともありますが、その時点で現在のビューコントローラーがどれなのか特定できないようです。

どなたか適切な対処法をご存じないでしょうか?私は明白な解決策を見逃していると確信しています。

解決方法は?

方法 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なのか、すぐにわかるような方法はないんだ。例えば、UINavigationControllerのデリゲートメソッドがあり、そこにUIViewControllerが提示されたときにそれを見つけることができる。このようなものを使って、最新のUIViewControllerが提示されたことを追跡することができる。

更新情報

UIをレイアウトする際に、様々なビットに適切なオートサイズ・マスクを適用すれば、UIの「手動」レイアウトに対処する必要さえない場合もあります...。