【Swift UIKit】AutoLayoutとは?NSLayoutConstraint/NSLayoutAnchorの使い方
この記事からわかること
- SwiftのUIKitでビューを真ん中に配置する方法
- AutoLayoutを使用して相対的に位置を指定する方法
- AutoLayoutをStoryboard(Interface Builder)に中心に配置する方法
- エラー:Unable to activate constraint with anchorsの原因
- NSLayoutConstraint/NSLayoutAnchor/Visual Format Languageの使用方法
index
[open]
\ アプリをリリースしました /
環境
- Xcode:16.3
- iOS:18.4
- Swift:6
- macOS:Sequoia 15.4
参考文献:Programmatically Creating Constraints
AutoLayoutとは?
「AutoLayout」とはiOSアプリにおいてビューのサイズや位置を動的に計算して表示させるための機能のことです。
iOSのデバイスは機種によって画面のサイズが異なるので絶対的な数値を使ってサイズや位置を指定してしまうと想定していないデバイスで使用された際や画面の向きを変更した際にレイアウトが崩れたり見た目が悪くなってしまいます。これを解決できるのがAutoLayoutです。
制約:Constraint
AutoLayoutではビューのサイズや位置を「制約(Constraint)」を用いて指定します。ここでいう制約とは「ビューの両端から画面まで距離は常に0pixel」や「X軸方向の中心にビューを固定する」と言ったルールを定義する感じです。
AutoLayoutはStoryboard(Interface Builder)からでもコードからでも指定することができますが、Apple公式からはInterface Builderでの設定が推奨されています。また複雑すぎる制約をかけてしまうと汎用性がなくなり、修正が困難になるので制約はできるだけ少なくすることがおすすめされています。
AutoLayoutはUIKitフレームワークを使用している場合に使用します。Swift UIの場合のレイアウト方法は異なります。
Storyboardから使用する
StoryboardからAutoLayoutを使用するにはInterface Builderの右下にある以下のアイコン部分から指定します。
一番左のアイコンはAutoLayoutで指定された位置にビューも戻すボタンです。AutoLayoutで制約をかけた後にマウスなどで動かしてしまうと実際の表示(制約がかかった状態)と齟齬が生まれるのでこのボタンでリセットすることができます。
ビューを揃える
この5つ並んだアイコンのうち2つ目と3つ目を主に操作しながらAutoLayoutを使用していきます。例として画面にラベルビューを追加して制約を設けてみます。2つ目のアイコンをクリックすると以下のようなポップアップが表示されます。
この状態で指定できるのは「Horizontal Center in Container」と「Vertical Center in Container」のみです。これは画面の中にビューが1つしか存在しないためです。2つ目のアイコンは複数のビューの間で制約をつけるので1つの場合はつけれる制約に限りがあります。指定できる項目は以下の通りです。
- Leading Edges ・・・左端揃え
- Trailing Edges ・・・右端揃え
- Top Edges・・・上端揃え
- Bottom Edges・・・下端揃え
- Horizontal Centers・・・X軸方向の中心揃え
- Vertical Centers・・・Y軸方向の中心揃え
- First Baselines・・・ベースライン揃え
- Horizontal Center in Container・・・親ViewのX軸方向の中心からの距離
- Vertical Center in Container・・・親ViewのY軸方向の中心からの距離
「Horizontal Center in Container」と「Vertical Center in Container」にチェックを入れて「Add Constraint」をクリックすると制約が適応されます。この場合は親ビューに対して中心位置にビューを配置します。
複数のビューがある場合
ラベルビューをもう1つ配置して、2つを選択した状態で2つ目のアイコンをクリックすると以下のように全ての項目が選択可能になっています。例えば「Leading Edges(左端揃え)」を指定すると以下のように左端にラインが表示されビューが揃った配置になります。
ビュー間の距離やサイズを定義する
3つ目のアイコンではビュー間の距離やビュー自体のサイズを定義することができます。
- Spacing to nearest neighbor・・・隣のViewとの距離
- Width・・・横幅
- Height・・・高さ
- Equal Widths・・・同じ幅にする
- Equal Heights・・・同じ高さにする
- Aspect Ratio・・・アスペクト比を固定
追加した制約を確認/削除する
追加した制約はView Controller Sceneの中のConstraintsから確認することができます。選択して「backspace」キーを押すと削除することができます。
また制約を選択すると画面上に対象の制約が表示されそこをダブルクリックすると編集することも可能です。
コードから使用する
コードからAutoLayoutを行うためには対象のビューに対して行わなければいけないことがあります。
- translatesAutoresizingMaskIntoConstraintsをfalseにする
- addSubview(ビューを追加しておく)
この2つをやっておかないとAutoLayoutが効かなかったりエラーを引き起こす原因になるので注意してください。
translatesAutoresizingMaskIntoConstraintsプロパティ
translatesAutoresizingMaskIntoConstraintsはビューの配置に対してAutoresizingMaskかAutoLayoutのどちらを使用するか指定するプロパティです。falseを指定する場合は、AutoLayoutになります。
具体的にはビューのframe(x, y, width, height)をframeやbounds、centerプロパティから直接操作することが可能かどうかを指定しています。trueにすると直接操作でき、falseにするとAutoLayoutの管理下になります。コードからビューを追加すると自動的にtrueにInterface Builderから追加すると自動的にfalseに設定されるのでコードからAutoLayoutを行うためには明示的にfalseにしておく必要があります。
targetView.translatesAutoresizingMaskIntoConstraints = false
Unable to activate constraint with anchors
AutoLayoutを使用したい場合はそのビューが既にビュー階層に追加されている必要があります。その前に制約を適応させようとすると以下のようなエラーを吐いてしまうので先にaddSubviewメソッドを使用してビューに追加しておきます。
"Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x6000010b1b40 \"UILabel:0x15fd09970.centerY\"> and <NSLayoutYAxisAnchor:0x6000010b3300 \"UIView:0x15fe07f50.centerY\"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal."
view.addSubview(targetView)
制約(Constraint)の定義方法
SwiftではコードでAutoLayoutを適応させるための制約を定義する方法が3つ用意されています。
- NSLayoutConstraint
- NSLayoutAnchor
- Visual Format Language
コードで制約を定義したらコード内からその制約を有効(active)にする必要があります。Activeになっていない制約は適応されないので注意してください。
NSLayoutConstraintクラス
NSLayoutConstraintクラスは2つのビューに対して制約を定義するクラスです。制約はイニシャライザを使用して定義します。しかしこのクラスでの定義は可読性が低く、全てパラメーターに値を設定する必要があるため実用的ではありません。Apple公式からは次に紹介するNSLayoutAnchorクラスの使用が推奨されています。
convenience init(
// 制約の左側のビュー
item view1: Any,
// 制約の左側の属性
attribute attr1: NSLayoutConstraint.Attribute,
// 制約の左側と制約の右側の関係
relatedBy relation: NSLayoutConstraint.Relation,
// 制約の右側の属性
toItem view2: Any?,
// 制約の右側の属性
attribute attr2: NSLayoutConstraint.Attribute,
// 変更された属性を取得することの一部として、制約の右側にある属性を乗じた定数。
multiplier: CGFloat,
// 制約の右側にある乗算された属性値に追加された定数は、最終的に変更された属性を生成します。
constant c: CGFloat
)
親ビューの中心にビューを配置する制約
NSLayoutConstraint(
item: label,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1,
constant: 0
).isActive = true
NSLayoutConstraint(
item: label,
attribute: .centerY,
relatedBy: .equal,
toItem: view,
attribute: .centerY,
multiplier: 1,
constant: 0
).isActive = true
イニシャライザを使用し定義したら最後にisActiveプロパティにtrue渡すことで有効にします。
NSLayoutAnchorクラス
NSLayoutAnchorクラスはコード量を抑えつつ、簡単に制約を定義することができます。ビューに元々定義されている各アンカープロパティに値を設定することでビューに制約を設定します。制約は各アンカープロパティからconstraintメソッドを呼び出して定義します。
親ビューの中心にビューを配置する制約
label.centerYAnchor.constraint(
equalTo: view.centerYAnchor
).isActive = true
label.centerXAnchor.constraint(
equalTo: view.centerXAnchor
).isActive = true
指定できるアンカープロパティやconstraintメソッドにはいろいろ種類があります。
指定できるアンカープロパティ
| カテゴリ | アンカー | 説明 |
|---|---|---|
| 水平方向 | leadingAnchor | 左端(LTR/RTL対応) |
| trailingAnchor | 右端(LTR/RTL対応) | |
| leftAnchor | 左端(古い, RTL非対応) | |
| rightAnchor | 右端(古い, RTL非対応) | |
| centerXAnchor | 水平方向の中心 | |
| 垂直方向 | topAnchor | 上端 |
| bottomAnchor | 下端 | |
| centerYAnchor | 垂直方向の中心 | |
| firstBaselineAnchor | テキスト1行目のベースライン | |
| lastBaselineAnchor | テキスト最終行のベースライン | |
| サイズ | widthAnchor | 幅 |
| heightAnchor | 高さ |
constraintメソッドの種類
| メソッド | 説明 |
|---|---|
constraint(equalTo:) |
基準アンカーと等しい位置に制約する |
constraint(equalTo:constant:) |
基準アンカーと等しい位置に制約し、指定したオフセットを追加する |
constraint(greaterThanOrEqualTo:) |
基準アンカー以上の位置に制約する |
constraint(greaterThanOrEqualTo:constant:) |
基準アンカー以上の位置に、指定したオフセットで制約する |
constraint(lessThanOrEqualTo:) |
基準アンカー以下の位置に制約する |
constraint(lessThanOrEqualTo:constant:) |
基準アンカー以下の位置に、指定したオフセットで制約する |
constraint(equalToConstant:) |
固定サイズを指定する(例: width = 100) |
constraint(equalTo:multiplier:) |
他のアンカーのサイズに倍率をかけて制約する(例: width = other.width × 0.5) |
constraint(equalTo:multiplier:constant:) |
倍率に加えてオフセットを加える(例: width = other.width × 0.5 + 20) |
constraint(greaterThanOrEqualToConstant:) |
最小サイズを指定する |
constraint(lessThanOrEqualToConstant:) |
最大サイズを指定する |
先ほどと同じ制約を定義してもコード量が抑えることができるのでコードで指定する場合はこのクラスを使用するのがおすすめです。
Visual Format Language
Visual Format LanguageはASCIIアートのような文字列を使用して制約を定義する方法です。この方法はさらにコード量を抑えることができますが使用できる制約に制限があったり、実行するまでエラーか識別できなかったりなどデメリットも多いです。
let views = ["myView" : myView]
let formatString = "|-[myView]-|"
let constraints = NSLayoutConstraint.constraints(withVisualFormat: formatString, options: .alignAllTop, metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)
使用例
コードからAutoLayoutの使用すること自体が公式よりあまり推奨されていませんが、使用する場合はNSLayoutAnchorクラスを使用するようにします。
以下は実際に対象ビューを親ビューの中心に配置する制約を定義した例です。
let label = UILabel()
label.text = "Test"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
またNSLayoutConstraintクラスのactivateメソッドを使用すればNSLayoutAnchorで定義した制約をまとめてアクティブにすることも可能です。
NSLayoutConstraint.activate([
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。





