web-dev-qa-db-ja.com

setNeedsLayoutとsetNeedsUpdateConstraintsおよびlayoutIfNeededとupdateConstraintsIfNeeded

自動レイアウトチェーンは基本的に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メソッドも呼び出します

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

214
Andrea

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

  • setNeedsUpdateConstraintsは、今後updateConstraintsIfNeededへの呼び出しがupdateConstraintsを呼び出すようにします。
  • setNeedsLayoutは、今後layoutIfNeededへの呼び出しがlayoutSubviewsを呼び出すようにします。

layoutSubviewsが呼び出されると、updateConstraintsIfNeededも呼び出されるため、経験上、手動で呼び出す必要はほとんどありません。実際、レイアウトをデバッグするときを除いて、これを呼び出したことがありません。

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

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

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

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

  • 制約を直接操作した場合は、setNeedsLayoutを呼び出します。
  • オーバーライドされたupdateConstraintsメソッドの制約を変更するwouldする条件(オフセットやsmthなど)を変更した場合(制約を変更する推奨方法、btw)、setNeedsUpdateConstraints、そしてほとんどの場合、setNeedsLayoutその後。
  • すぐに効果を得るために上記のアクションのいずれかが必要な場合。レイアウトパス後に新しいフレームの高さを学習する必要がある場合は、layoutIfNeededを追加します。

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

255
coverback

カバーバックによる回答 はかなり正しいです。ただし、詳細をいくつか追加したいと思います。

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

UIView's Lifecycle

  1. -setNeedsLayoutの代わりに-setNeedsUpdateConstraintsを使用するとすべてが期待通りに動作することがわかりましたが、-layoutIfNeeded-updateConstraintsIfNeededに変更してもアニメーションは発生しません

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

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

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

updateConstraintsを呼び出すには、次の手順を実行する必要があります。

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];
81
Kunal Balani