コードからAutoLayoutの制約の有効化/無効化を切り替える方法は以下の4つがあります。
-
UIViewのadd(remove)Constraint(_:) -
UIViewのadd(remove)Constraints(_:) -
NSLayoutConstraintのisActive -
NSLayoutConstraintの(de)activate(_:)
この4つの方法の違いを解説します。
UIViewのadd(remove)Constraint(_:)
この方法は非推奨です!
UIViewのインスタンスメソッドのaddConstraint(_:)とremoveConstraint(_:)を使う方法です。
以下のように使います。
let widthConstraint = aView.widthAnchor.constraint(equalToConstant: 100)
aView.addConstraint(widthConstraint)
簡単ですね。
では、以下のようなViewの階層を考えてみます。
viewBとviewCはviewAのsubviewです。
ここで、「viewBとviewCの横幅が等しい」という制約を加えたいとしましょう。以下にコードを示します。
let widthEqualConstraint = viewB.widthAnchor.constraint(equalTo: viewC.widthAnchor)
viewA.addConstraint(widthEqualConstraint)
上記のコードで注意するべきことは、viewAに対してaddConstraint(_:)を呼んでいることです。
ここで間違えて、viewBやviewCに対して呼んでいたら、クラッシュするか制約が働かなくなります。つまり、
addConstraint(_:)は、制約に関連するviewに共通の親viewのうち最も階層の近いものに対して呼ばないといけません。そうしないと、クラッシュするか制約が効かなくなってしまいます。
先ほどの例だと、viewBとviewCの共通の親であるviewA対して呼ばなければいけません。ここが難しいところです。幸いなことに回避方法はあるので、後ほど紹介します。
removeConstraint(_:)について
removeConstraint(_:)は、制約を解除します。それ以外はaddConstraint(_:)と同じです。
UIViewのadd(remove)Constraints(_:)
このメソッドはNSLayoutConstraintの配列を引数にとります。制約を一度に有効化/無効化することができますが、add(remove)Constraint(_:)と同じく適切なviewに対してメソッドを呼ばなければならないという問題は残っています。したがって、このメソッドはおすすめできません。
NSLayoutConstraintのisActive
NSLayoutConstraintのプロパティのisActiveを使います。例を示します。
let widthConstraint = aView.widthAnchor.constraint(equalToConstant: 100)
widthConstraint.isActive = true
// aView.widthAnchor.constraint(equalToConstant: 100).isActive = ture でもOK
ここで再び以下の画像を考えてみます。viewBとviewCはviewAのsubviewです。
isActiveを使うとviewBとviewCの横幅が等しい制約を有効化する方法は以下のようになります。
let widthEqualConstraint = viewB.widthAnchor.constraint(equalTo: viewC.widthAnchor)
widthEqualConstraint.isActive = true
viewA.constraints.contains(widthEqualConstraint) // => true
3行目に注目してください。widthEqualConstraintがviewAに所属していることを示しています。このように、isActiveを使うとその制約がどのviewに所属するべきかを自動的に解決してくれます。制約を解除したいときはisActive = falseとしてやればOKです。非常に便利ですね。
NSLayoutConstraintの(de)activate(_:)
NSLayoutConstraintのクラスメソッドです。NSLayoutConstraintの配列を引数にとり、制約の有効化/無効化を一度に設定します。
このメソッドも、与えられた制約がどのviewに所属するかを自動的に解決してくれます。
結局どれを使えばいいの?
わかりやすく表にしました。
| 方法 | 所属するviewの自動解決 |
|---|---|
add(remove)Constraint(_:) |
❌ |
add(remove)Constraints(_:) |
❌ |
isActive |
⭕️ |
(de)activate(_:) |
⭕️ |
上の二つは所属するviewの自動解決をしてくれないので使うのをやめましょう。もし間違ったviewに対してメソッドを呼んでしまった時のリスクが大きすぎます。
下の二つなら正直に言ってどちらでも良いのですが、NSLayoutConstraintの(de)activate(_:)の方がパフォーマンスの点で有利です。これも表にしてみます。
| 方法 | パフォーマンス |
|---|---|
isActive |
⭕️ |
(de)activate(_:) |
💯 |
ドキュメントによると、
Typically, using this method is more efficient than activating each constraint individually.
と書いてあるので、NSLayoutConstraintの(de)activate(_:)の方がパフォーマンスが有利だと考えて良いです。
その理由は、WWDC 2014のMysteries of Auto Layout, Part 2の5:20 ~ 5:35で言及されています。一度に複数の制約を有効化/無効化することでレイアウトのライフサイクルが無駄に回らないのが理由のようです。
結論
有効化/無効化したい制約が一つだけならisActiveあるいはNSLayoutConstraintsの(de)activate(_:)のどちらを使っても良いです。ただし、複数の制約がある場合はNSLayoutConstraintsの(de)activate(_:)がパフォーマンスの点で有利です。
