趣味でiOSアプリ開発をかじっていた自分が、改めてiOS開発を勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。
参考文献
この記事は以下の書籍の情報を参考にして執筆しました。
##レイアウトが崩れる原因
レイアウトが一意に定まっていない、曖昧なレイアウト。
意図しない制約が反映されてしまう、制約のコンフリクトがある。
##曖昧なレイアウト
####レイアウトが曖昧かどうか調べる
hasAmbiguousLayout()を用いてレイアウトが曖昧かどうか調べることができる。
view.hasAmbiguousLayout
下記のようにviewの制約を設定している途中で実行するとtrueが帰ってくる。
hasAmbiguousLayout()がtrueの状態ではviewは表示されない。
let view1 = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 300, height: 250))
view1.backgroundColor = .red
view1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view1)
view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 200).isActive = true
view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100).isActive = true
view1.widthAnchor.constraint(equalToConstant: 200).isActive = true
print(view1.hasAmbiguousLayout) // true
view1.heightAnchor.constraint(equalToConstant: 200).isActive = true
print(view1.hasAmbiguousLayout) // false
#制約のコンフリクト
制約のコンフリクトはレイアウトエンジンによって自動的に不必要な制約が削除されることによって解決されるが、意図しないレイアウトとなることがあるのでレイアウトが崩れてしまうことがある。
##制約のコンフリクトをデバッグする
例として意図的に制約のコンフリクトを発生させる。
let view1 = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 300, height: 250))
view1.backgroundColor = .red
view1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view1)
view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 200).isActive = true
// 同じ要素かつ値が違う制約を宣言して制約をコンフリクトさせる
view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100).isActive = true
view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 200).isActive = true
view1.widthAnchor.constraint(equalToConstant: 200).isActive = true
view1.heightAnchor.constraint(equalToConstant: 200).isActive = true
####エラーメッセージを読み解いて確認
実行時のエラーメッセージにコンフリクトした制約の概要が記載されている。
VisualFormatLanguage形式で記載されている。
[16824:2982179] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600003802a30 H:|-(100)-[UIView:0x7fde9f408970] (active, names: '|':UIView:0x7fde9f40be70 )>",
"<NSLayoutConstraint:0x600003802a80 H:|-(200)-[UIView:0x7fde9f408970] (active, names: '|':UIView:0x7fde9f40be70 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003802a80 H:|-(200)-[UIView:0x7fde9f408970] (active, names: '|':UIView:0x7fde9f40be70 )>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
####ビューデバッガーで確認
ビューデバッガーでの表示。画面左からの距離の制約が2つあることがわかる。
それぞれ[右クリック]→[Print Discripption of ~]で制約を表示させると値を確認できる。
Printing description of $21:
<NSLayoutConstraint:0x600003802a30 H:|-(100)-[UIView:0x7fde9f408970] (active, names: '|':UIView:0x7fde9f40be70 )>
Printing description of $22:
<NSLayoutConstraint:0x600003802a80 H:|-(200)-[UIView:0x7fde9f408970] (active, names: '|':UIView:0x7fde9f40be70 )>
##VisualFormatLanguageを読みやすくするツール
https://www.wtfautolayout.com/
####Viewの名称
indentiferなどに部品の名称を設定することでエラーメッセージの「0x7fde9f408970」こういうのを読みやすくできる。