2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

崩れたレイアウトの調査方法

Posted at

趣味で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 ~]で制約を表示させると値を確認できる。
image.png

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」こういうのを読みやすくできる。

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?