viewWillAppear:とviewDidAppear:の間のビューフレームの変更


85

私は、接続されているが、私のアプリケーションで奇妙な行動を発見したIBOutletに私のビューコントローラに呼び出し間で接続されているビューのフレームを持っているのviewWillAppear:viewDidAppear:。これが私のUIViewControllerサブクラスの関連コードです:

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

-(void)viewDidAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

および結果のログ出力:

MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>

これは、2つの呼び出し間でフレームが変化していることを明確に示しています。viewDidLoadメソッド内のビューを使用してセットアップを行いたかったのですが、画面に表示されるまでコンテンツを変更できない場合は、まったく役に立たないようです。何が起こっているのでしょうか?


2
自動レイアウトを使用していますか?このビューをInterfaceBuilderに追加しますか、それともプログラムで追加しますか?
アンドレア

自動レイアウトが有効になっており、このビューはストーリーボードからIBで作成されます。
Jumhyn 2013

1
ストーリーボードを使用したことはありませんが、おそらく正しいです。ビューの自動レイアウトフレームの使用は、自動レイアウトエンジンが計算を開始するときに設定されます。ビューコントローラの-(void)viewDidLayoutSubviewsmpethodのスーパーの直後に同じことを尋ねてみてください。
アンドレア

これにより、適切なタイミングでイベントが正常にトリガーされますが、ビューでアニメーションを実行するたびにそのメソッドも呼び出されます。
Jumhyn 2013

1
viewDidLayoutSubviews正しい道でした。メインビューのフレームを変更するたびにメソッドが呼び出されないように、すべてのコンテンツをサブビューに配置する必要がありました。
Jumhyn 2013

回答:


111

AutolayoutビューのGUIの設計と開発の方法に大きな変更を加えました。主な違いの1つは、autolayoutビューサイズをすぐに変更せず、トリガーされたとき、つまり特定の時間にのみ変更することですが、制約をすぐに再計算するか、レイアウトが「必要」としてマークするように強制できます。のように機能し-setNeedDisplayます。
私にとっての大きな課題は、それを理解して受け入れることでした。自動サイズ変更マスクを使用する必要がなくなり、フレームはビューを配置する上で役に立たないプロパティになりました。もはやビューの位置を考える必要はありませんが、お互いに関連した空間でそれらをどのように見たいかを考える必要があります。
古い自動サイズ変更マスクと自動レイアウトを混在させたい場合は、問題が発生したときです。自動レイアウトの実装についてはすぐに検討し、自動レイアウトに基づくビュー階層で古いアプローチを混在させないようにする必要があります。
ビューコントローラのメインビューなど、自動サイズ変更マスクのみを使用するコンテナビューを作成することは問題ありませんが、混合しない方がよいでしょう。
ストーリーボードを使用したことはありませんが、おそらく正しいです。自動レイアウトを使用すると、自動レイアウトエンジンが計算を開始するときに、ビューのフレームが設定されます。- (void)viewDidLayoutSubviewsビューコントローラのメソッドのスーパーの直後に同じことを聞いてみてください。
このメソッドは、自動レイアウトエンジンがビューのフレームの計算を終了したときに呼び出されます。


5
-(void)viewDidLayoutSubviewsが私にとっての答えです!どうもありがとう!
FrizzTheSnail 2015

この答えは本当に正しくありません。はい、もちろん、明らかに(5?)年間、自動レイアウトを使用する必要があります。ただし、(自動レイアウトを使用して)画面に何かを「ユーザーに表示される直前に」追加する必要がある状況はいくつもあります。(viewDidAppearで実行すると、ちらつきが発生します。viewWillAppearで実行すると、位置が間違ってしまいます。)実際の答えは、実際にはviewDidLayoutSubviewsを使用することです。
Fattie 2017

add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews....あなたはそのビューを何度も見せようとしています
アンドレア

156

ドキュメントから

viewWillAppear:

ビューがビュー階層に追加されようとしていることをビューコントローラに通知します。

viewDidAppear:

ビューがビュー階層に追加されたことをビューコントローラに通知します。

その結果、サブビューのフレームはまだviewWillAppear:に設定されていません。

ビューが画面に表示される前にUI変更する適切な方法は次のとおりです。

viewDidLayoutSubviews

ビューがサブビューをレイアウトしたことをビューコントローラに通知します。


2
うーん、それは迷惑です。ドキュメントの文言でさえ、viewWillApepar:とviewDidAppear:が次々に発生するはずであるかのように見えます。
Jumhyn 2013

この答えを待っている2ヶ月...私はそれを見つけてくれてありがとう!! どうもありがとうございました!
ラファエル・ルイスムニョス

6
これviewDidLayoutSubviewsは複数回呼び出され、常に同じフレームで呼び出されるとは限らないことに注意してください(最初の呼び出しでCGRectZeroを使用して呼び出されることがあると思います)。追加されたサブビューおよびビューの他の変更ごとに呼び出されます。
bauerMusic 2015年

私は一日中これに突っ込んでいて(viewDidLayoutSubviewsはビューを閉じるときを含めて複数回呼び出されます)、それでefを言って、ロジックをifステートメントに入れてboolを作成しました。コードは1回実行されました。よりクリーンな方法が必要だと思いますが、すでに時間がかかりすぎています。
ソレノイド

私たちはこれの底に到達しなければなりません!viewDidLayoutSubviewsはひどいです、seNeedDisplayは機能しません
Yaro

9

コール

self.scrollView.layoutIfNeeded()

あなたのviewWillAppear方法で。その後、そのフレームにアクセスでき、印刷したときと同じ値になりますviewDidAppear


1
いいえ、これは正しくありません。例:(ナビゲーションコントローラーから)画面にナビゲーションバーがあります。layoutIfNeeded()の後でも、ナビゲーションバーの高さが含まれていないため、フレームサイズが変更されます。
xaphod 2017年

これは、IS scrollViewフレームはそのスーパービュー内の制約に縛られている場合、それはビューのレイアウト呼び出し階層全体で一貫しているように、scrollviewフレーム寸法の再計算を強制するための良い方法。
smakus

4

私の場合、すべてのフレーム関連メソッドをに移動します

override func viewWillLayoutSubviews()

完全に機能しました(ストーリーボードから制約を変更しようとしていました)。

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