setNeedsLayoutとsetNeedsUpdateConstraintsおよびlayoutIfNeededとupdateConstraintsIfNeeded


227

自動レイアウトチェーンは基本的に3つの異なるプロセスで構成されることを知っています。

  1. 制約の更新
  2. レイアウトビュー(ここでフレームの計算を取得します)
  3. 表示

何は、私には全く明らかではないとの内側の差です-setNeedsLayout-setNeedsUpdateConstraints。Apple Docsから:

setNeedsLayout

ビューのサブビューのレイアウトを調整する場合は、アプリケーションのメインスレッドでこのメソッドを呼び出します。このメソッドはリクエストを記録し、すぐに戻ります。このメソッドは即時の更新を強制せず、代わりに次の更新サイクルを待機するため、これらのビューを更新する前に、このメソッドを使用して複数のビューのレイアウトを無効にすることができます。この動作により、すべてのレイアウト更新を1つの更新サイクルに統合できます。これは通常、パフォーマンスの点で優れています。

setNeedsUpdateConstraints

カスタムビューのプロパティが制約に影響を与える方法で変更された場合、このメソッドを呼び出して、将来のある時点で制約を更新する必要があることを示すことができます。次に、システムはupdateConstraintsを通常のレイアウトパスの一部として呼び出します。制約が必要になる直前に一度に更新することで、レイアウトパス間でビューに複数の変更が加えられたときに、不必要に制約を再計算する必要がなくなります。

制約を変更した後でビューをアニメーション化し、通常は変更をアニメーション化する場合は、たとえば次のようにします。

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

すべてを使用するのでは-setNeedsLayoutなく-setNeedsUpdateConstraints期待どおりに機能する場合は、を使用して変更-layoutIfNeededする-updateConstraintsIfNeededとアニメーションが発生しないことがわかりました。
私は自分の結論を出そうとしました:

  • -updateConstraintsIfNeeded 制約を更新するだけで、レイアウトを強制的に実行しないため、元のフレームは保持されます
  • -setNeedsLayout-updateContraintsメソッドも呼び出す

では、一方を他方の代わりに使用してもよいのはいつですか?レイアウトメソッドについては、制約が変更されたビューまたは親ビューで呼び出す必要がありますか?


27
私は人々が反対票を投じていることを理解していません...本当に。だから、強制的に理由を尋ねたり、まったく意味がないなど、それについて何かする必要があります
Andrea

7
おそらく、彼らは批評家のバッジ(最初の反対票)を取得する必要があるだけでしょう
fujianjin6471

1
こちらをご覧になることを強くお勧めします。答えは、実際の問題の解決策です。また、このビデオを
Honey

回答:


258

あなたの結論は正しいです。基本的なスキームは次のとおりです。

  • setNeedsUpdateConstraints将来の呼び出しを確認しupdateConstraintsIfNeededますupdateConstraints
  • setNeedsLayout将来の呼び出しを確認しlayoutIfNeededますlayoutSubviews

ときにlayoutSubviews呼ばれて、それはまた、呼び出したupdateConstraintsIfNeededので、それは手動でめったに私の経験で必要とされていない呼び出し、。実際、レイアウトをデバッグするとき以外は、これを呼び出したことはありません。

を使用して制約を更新することsetNeedsUpdateConstraintsも非常にまれです。objc.io–aはautolayouts–saysについて読む必要があります

後で何かが変更されて制約の1つが無効になった場合は、制約をすぐに削除して、setNeedsUpdateConstraintsを呼び出す必要があります。実際、これが制約更新パスをトリガーする必要がある唯一のケースです。

さらに、私の経験でsetNeedsLayoutは、新しい制約はほとんど新しいレイアウトを要求しているため、制約を無効にしたり、コードの次の行にを設定したりする必要はありませんでした。

経験則は次のとおりです。

  • 制約を直接操作した場合は、を呼び出しますsetNeedsLayout
  • あなたは(オフセットまたはなめらかなど)いくつかの条件に変更した場合でしょう、あなたのオーバーライドされた内の制約に変更updateConstraints(ところで、変更の制約に推奨される方法)メソッドをコールsetNeedsUpdateConstraintsし、ほとんどの時間、setNeedsLayoutその後に。
  • 上記のアクションのいずれかをすぐに有効にする必要がある場合(たとえば、レイアウトパスの後で新しいフレームの高さを学習する必要がある場合)には、を追加しますlayoutIfNeeded

また、アニメーションコードでsetNeedsUpdateConstraintsは、制約はアニメーションの前に手動で更新され、アニメーションは古いビューと新しいビューの違いに基づいてビューを再レイアウトするだけなので、不要であると思います。


@coverbackでobjc.ioは、「後で何かが変更されて制約の1つが無効になる場合は、制約をすぐに削除してsetNeedsUpdateConstraintsを呼び出す必要があります。実際、これが制約更新パスをトリガーする必要がある唯一のケースです。」そして、Animationブロックでは、constraint.contantを削除、追加、または変更するときに、setNeedsLayoutを呼び出す必要があると表示されています。違いは何ですか?私は本当の愚かさを感じます:(
pash3r 2014

3
@ pash3r違いは、定数を更新しても「無効化」と見なされないことです。無効化とは、別のビューにアタッチしたり、完全に削除したりする必要があるなど、関連性がなくなった場合です。定数は単にビューを近づけたり遠ざけたり、サイズを変更したりするだけなので、の必要性ですsetNeedsLayout
カバーバック2014

@coverback setNeedsLayoutlayoutSubviews、次の更新サイクルで呼び出されることを確認しますが、これはおそらく何の関係もありませんlayoutIfNeededか?
fujianjin6471 2015年

2
@coverback制約を直接操作layoutSubviewsすると、自動的に呼び出されます。呼び出す必要はありませんsetNeedsLayout
fujianjin6471

はい、制約のプロパティを直接操作するとがトリガーされるlayoutSubviewsため、手動で行う必要はありません。ただし、呼び出す必要がありますlayoutIfNeeded変更を有効にするのではなく、ただちに次のレイアウトサイクルが必要な場合
チャーリー・マーティン

89

coverbackによって答えはかなり正確です。しかし、私はいくつかの詳細を追加したいと思います。

以下は、他の動作を説明する典型的なUIViewサイクルの図です。

UIViewのライフサイクル

  1. すべてを使用するのでは-setNeedsLayoutなく-setNeedsUpdateConstraints期待どおりに機能する場合は、を使用して変更-layoutIfNeededする-updateConstraintsIfNeededとアニメーションが発生しないことがわかりました。

updateConstraints通常は何もしません。layoutSubviews呼び出されるまで適用されない制約を解決するだけです。したがって、アニメーションにはへの呼び出しが必要layoutSubviewsです。

  1. setNeedsLayoutも-updateContraintsメソッドを呼び出します

いいえ、これは必要ありません。制約が変更されていない場合、UIViewはへの呼び出しをスキップしますupdateConstraintssetNeedsUpdateConstraintプロセス内の制約を変更するには、明示的に呼び出す必要があります。

電話をかけるupdateConstraintsには、次のことを行う必要があります。

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];

おかげで、これで私の問題は解決しました。アニメーションの前にLayoutIfNeeded()を呼び出すと一時的な制約が追加される親UIViewのないUIWindowがありました。UIWindowにサブビューラッパーを追加し、UIWindowでこれらの3つのメソッドを呼び出すと、問題が解決しました。
masterwok 2016

setNeedsLayoutの直後にlayoutIfNeededを呼び出すのは正しいとは思いません。レイアウトがすぐに再描画され、次の更新サイクルで2番目のレイアウトが再描画されるという事実にもかかわらず、メソッドは同じことを行うためです。
2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.