2015-11-26 5 views
5

まず、これは主に効率の問題です。`viewDidLayoutSubviews`のフレーム計算

viewWillAppearが早すぎてviewDidAppearが遅すぎる(表示は既に表示されている)フレーム計算を行う場所については、多くの議論があります。

一般的な答えは、viewDidLayoutSubviewsでフレーム計算を行うことです。問題は、複数回呼び出されることです。さらに悪いことに、最も正確なコール、つまりすべてのフレームが最終サイズを持つコールが最後のコールです。私の知る限り、どのような呼び出しが最終的なものかを知る方法はありません。

frameAreSetフラグ(初期化済みの偽)とチェックがあります。フレームがゼロでない場合(self.view.frame.size.width != 0など)、 'framesAreSet'がfalseの場合は、フラグが入り、1回だけ計算されます。ような何か:

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    if (self.view.frame.size.width != 0 && !framesAreSet) 
    { 
     framesAreSet = true; 

     //Calculate frames here 
    } 
} 

これは大丈夫に見えますが、真実で、self.view.frame.size.width != 0のようなもののチェックがフレームが実際に設定されていることをない保証を行います。 viewDidLayoutSubviewsが後で呼び出されるという事実は、いくつかのフレームが最終状態に設定されていないことを示唆しています。

viewCompleteLayoutSubviewsを持っているとよいでしょう。すべてのフレームが設定され、ビューがまだ表示されていないときに、「1回限り」のフレーム計算を行う最良の方法は何ですか?

(これはおそらくNSConstraintsを使用してビューないの問題です)

+1

'viewDidLayoutSubviews'が複数回呼び出されるのはなぜですか?あなたはどんな問題に直面していますか? – rmaddy

+0

ここで 'self.view'の範囲を更新していますか?もしそうなら、あなたの問題です。 – Clafou

+0

@rmaddy冗長性は良いコーディング方法ではありません。複雑なViewControllerを持っていて、コード内に多くのサブビューを追加し(実際にはほとんどコードで作成されます)、設定フレームのための多くの計算を行います。実際には、「重い」計算を呼び出すのは、サブビューを追加するだけで何度も呼び出すことを想像してください。最後のものを除くすべてが冗長です。時には「それは働く」ことが十分ではない。 – bauerMusic

答えて

1

アプリは描画ごとに1つだけdidLayoutSubviewsを取得します(その時点まで、ビューの状態の変更はちょうどsetNeedsLayoutと記載されています)。その意味で、didLayoutSubviewsは、didCompleteLayoutOfSubviewsです。描画後、別のビューステートの変更が発生した場合、レイアウトは再び不完全です。

つまり、didLayout呼び出しの数は、サブビューの追加やフレームの変更の数に依存しません。描画回数によって異なります(実行ループと混同しないでください)。描画の前に、needsLayoutフラグが設定されていると、viewSubviewsとdidLayoutSubviewsは、ビュー階層がどのくらい並べ替えられても、1回だけ呼び出されます。

+0

私が正しく理解していれば、 'didLayoutSubviews'の呼び出しの数は、私の変更(フレームの変更、サブビューの追加)のコードに依存します(最初のものとは別に)。つまり、予測可能で最後の 'didLayoutSubviewsトリガー'の後にフラグを設定することができます。その後、 'didLayoutSubviews'が最後の時間(そのサイクルのために)呼び出されますか? – bauerMusic

+1

私はあなたの理解がかなり正しいとは思わない。 didLayout呼び出しの数は、サブビューの追加またはフレームの変更の数に依存しません。描画の回数によって異なります(実行ループと混同しないでください)。描画の前に、needsLayoutフラグが設定されていると、viewSubviewsとdidLayoutSubviewsは、ビュー階層がどのくらい並べ替えられても、1回だけ呼び出されます。 – danh

+0

良い答え。最後のコメントをあなたの答えに加えることができれば嬉しいです、私はそれを「受け入れられる」とマークします。最初の 'layoutSubviews'の後に描画するビューがどこにあるかを調べる必要があります。しかし、これはやや異なる質問です。ありがとう! – bauerMusic

0

あなたはフレームが一度初期化されている場合、これはあなたの内部で取得することはありませんことを一度トークンディスパッチ(GCD)を使用してviewDidLayoutSubviews方法でフレームを計算することができます発送ブロック。

+0

素晴らしい点。しかし、私の目的のために、フラグとは異なる動作をしません。また、最後に更新された 'viewDidLayoutSubviews'コールを '捕まえる'という問題もあります。 – bauerMusic

+0

しかし、私はviewDisLayoutSubviewの最後の呼び出しを得ることができるとは思わない....あなたはviewWillAppearでフレーム計算を行うことができます。 – Abhishek

+0

私が書いたように、異なる条件では、 'viewWillAppear'フレームは最終的なサイズを持っていません。 – bauerMusic

1

ビューに現在存在するディメンションのレイアウトを変更する必要があります。

フレームを設定してから、デバイスを回転させます。あなたはフレームを再計算する必要がありますか?確かに、境界のサイズが変更されている!あなたはframesAreSetで危険な前提をしています。

パフォーマンスが本当に重要である場合は、計算されたフレームをキャッシュして、境界がサイズに変更された場合にそれらを無効にする辞書を作成することができます。

しかし、確かにレイアウトを1回実行することで逃げることはありません。

代わりに、ビューコントローラのviewをカスタムクラスのインスタンスにして、そこに-layoutSubviewsをオーバーライドすることもできます。

+0

_ "あなたのフレームセットで危険な仮定をしています" _。ある時点で、 'viewDidAppear'のように設定されます。回転しないビューを考えてください。同じフレームの計算を複数回実行するのは意味がありますか?さらに悪いことに、ビューが回転する場合は、すべての回転がフレーム計算を複数回実行する必要があります。 – bauerMusic

+0

"回転しないビューを考えてください。同じフレームの計算を複数回実行するのは意味がありますか?" - はい、ビューのサイズを変更する原因となる他のものがあります。 –