ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

読んだ : よくわかる Auto Layout — iOS レスポンシブデザインをマスター

同僚の iOS エンジニア氏に 「Auto Layout についてちゃんと学んでおくといいですよ」 って言われたので読んだ。

よくわかるAuto Layout  iOSレスポンシブデザインをマスター

よくわかるAuto Layout iOSレスポンシブデザインをマスター

著者によると下記のように書かれていて、わりと iOS アプリ開発の初心者向けっぽい。 まさに iOS アプリ開発初心者である自分にとって学びの多い良い本だった。

「よくわかるAuto Layout」はAuto Layoutとサイズクラスについて解説している書籍です。レイアウトの基礎を包括的に紹介し、後半では業務でよく使うパターンをまとめています。アマゾンの紹介文によると対象者は、

過去一度はXcodeを用いてアプリを作ったことがあるが、Auto Layoutとサイズクラスを用いたAdaptive Layoutと言われると、つい尻込みしてしまうアプリ開発者にぴったりの一冊です。

という感じになっています。基本的には、iOSアプリ開発初心者に向けて書いているので、"黒帯エンジニア"みたいな人には向きません。

「よくわかるAuto Layout」を執筆した話 - Jeffsuke is not a pen.

2016 年の本なので最新情報がない点だけは注意が必要そう。 (とはいえ Auto Layout の基礎を学ぶ上では問題なさそう。)

学んだこと

大量の学びがあった。

1 章 : Adaptive Layout をはじめる

  • 「Adaptivity」 というのは web でいうところのレスポンシブデザインみたいな感じ。 様々なサイズの画面などに適合するように UI デザインする
  • iOS 8 からトレイト (trait) という概念が導入された。 画面サイズなどの環境情報を抽象的なオブジェクトとして扱う概念

2 章 : Auto Layout の基本概念

  • Auto Layout エンジンは制約式の連立方程式を解いてレイアウトを決定する
    • Auto Layout エンジンによる計算においては、各コンポーネントのフレームではなく外接矩形 (Alignment Rectangle) が用いられる
    • 制約式は Item1.Attribute1 = Multiplier * Item2.Attribute2 + Constant の形。 NSLayoutConstraint で表現される
      • Constant (制約定数) は制約生成後にも変更可能で、画面回転等に対応しやすい
      • 等号だけでなく不等号も可能
    • 制約には優先度がある
    • 参考 : Auto Layout Guide: Anatomy of a Constraint
  • Intrinsic Content Size は、view が保持する内容の固有のサイズ。 他の制約がない場合にはこのサイズが使われたりする
    • Content Hugging Priority と Content Compression Resistance Priority で伸び縮みの優先度が決められる
  • Auto Layout 以前には Autoresizing が使われていた。 Auto Layout にも影響する (NSAutoresizingMaskLayoutConstraint という制約になる)。 View をコードで生成するとデフォルトでは Autoresizing が Auto Layout の制約に変換されるので注意。 (UIView.translatesAutoresizingMaskIntoConstraints で変換されないように設定できる)

3 章 : UIViewController とレイアウトをサポートするクラス

  • 表示の階層構造 : スクリーン (UIScreen)、アプリのウィンドウ (UIWindow)、ビューコントローラ、ビューコントローラが持つビュー
  • アプリ起動時のルートビューコントローラの設定方法はストーリーボードを使うかどうかで異なる
  • レイアウトのライフサイクルは、制約の更新、フレームの更新、レンダリングの 3 ステップ
  • フレームとは??
  • View controller のライフサイクル : 読み込み、表示、レイアウト、非表示
  • UIWindow はウィンドウ。 キーボードとかもウィンドウ
  • アプリ内部でウィンドウの配列も取れる。 windowLevel で重なり順
  • ウィンドウサイズとスクリーンサイズは区別すること

4 章 : Storyboard と Auto Layout

  • アプリ起動時のストーリーボードをプログラムで指定もできる。 A/B テストとか。 AppDelegate で
  • ストーリーボードごとに Auto Layout の有効無効の設定ができる
  • 複数ビューに Spacing to nearest neighbor もできる
  • Equal Width などで 2 つのビューにサイズの制約をつけてそれ以外の幅の制約がない場合は大きい方の Intrinsic content size が使われる (Content Hugging Priority と Content Compression Resistance Priority だと後者のほうが通常は優先)
  • Align Panel でビュー間の並び方を決定。 定数も指定可能
  • Stack ボタン、本書の例とは変わってそう?
  • IB (Interface Builder) でビューを Control ドラッグすることでも制約追加できる。 Document Outline Control 上でも
  • IB でのビューのマージン表示 : Editor > Canvas > Show Layout Rectangles
  • IB 上で追加した制約は IBOutlet で参照できる
  • 制約に Identifier をつけることができて、エラーメッセージなどがわかりやすくなったりコードから参照できるようになったり
  • xib で独自ビューを作れる
  • iOS 8 以降の端末のみサポートであれば Xcode 7 で導入された Storyboard Reference で分割できる。 さもなければコードで分割
  • Bundle の概念とは? → アプリやフレームワーク、その他の種々のコンテンツなどを表すものらしい。

5 章 : コードと Auto Layout

  • 制約 (NSLayoutConstraint) をコード上で生成する方法は 3 つ。 普通にインスタンス化するのと、VFL (Visual Format Language) を用いる方法と、NSLayoutAnchor ファクトリークラスを用いる方法
  • VFL はアスキーアートみたいなやつ。 デバッグ中のエラーメッセージにも出てくるので一応形式は知っておくとよいとのこと
  • NSLayoutAnchor がおすすめ。 iOS 9.0 以降
  • 制約を作ったあとは有効化が必要。 iOS 7 以前のサポートが必要ならビューに追加する。 iOS 8 以降だけでいいなら制約の有効化を行う。 複数制約を有効化するための便利メソッドもある (NSLayoutConstraint.activateConstrains)
  • 制約の削除や編集もできる。 編集で制約定数を変更するほうが制約の削除と追加よりコストが低いらしい
  • iOS 9.0 以降では UILayoutGuide で空間を表現できる。 AndroidSpace っぽいけど、Space と違って view ではないらしい。 Xcode 7.3 時点では IB 上で生成できない (まじか)

6 章 : 実装基本パターン

  • 親ビューに対する割合で幅や高さを指定できる。 Multiplier で。
  • Android でいうところの Space みたいなものは UIView を透明にする (または上に書いた UILayoutGuide)
    • 透明にするほうがパフォーマンス悪いから背景色決まってるなら背景色指定の方が良いらしい
  • View を削除したり、一時的に非表示にして後から戻すというような処理 (本書ではトルツメ) は難しい
    • View を削除するとその view に関連する制約も削除されるため
  • UIScrollView の内容サイズも Auto Layout で指定する (さもなければコードで指定する必要がある)
  • 上述のとおり、AutoresizingMask が制約に変換されないようにした方が (暗黙的に制約が追加されて混乱する、ということがないので) 良い場合が多いが、xib ファイルにレイアウトがカプセル化された view を UITableCellView に追加する場合などは Autoresizing を有効にしておくと便利っぽい (そうすると親 view のサイズに自動的に合うようになるので)
  • UIStackView は、view を縦か横に一列に並べる view。 制約を自分で追加せずとも自動で Auto Layout してくれるので便利 (Android でいうところの LinearLayout っぽい感じ)

7 章 : 実装応用パターン集

  • UITableView のセルの高さを指定する方法は 3 つ。 固定の高さを与える方法と、セルごとに計算する方法と、Self-Sizing Cells を使う方法 (iOS 8 以降)。
  • オフスクリーンでのレイアウトの際に UILabel のサイズを見るには preferredMaxLayoutWidth を設定する必要がある
  • テーブルのスクロール領域全体の高さ計算が最初に実行されるので、セル数が多い場合はパフォーマンスに問題が起こりうる。 iOS 7 以降だと estimatedRowHeight で対処できる
  • Dynamic Type、ユーザー設定に合わせてフォントのサイズを変更する仕組み
  • キーボード表示時に表示中の内容がキーボードに隠れないようにするには独自処理が必要
  • 画面回転への対応は、iOS 8.0 以降とそれより前で方法が異なる
    • 回転時のレイアウト変更は制約の有効化と無効化でやるのが (制約の追加と削除よりも) パフォーマンス的には良い
    • UIViewControllerTransitionCoordinator.animateAlongsideTransition に制約の変更を記述することで、画面回転のアニメーションと同期して動くようになるらしい

8 章 : Auto Layout をデバッグする

  • デバッグに用いることができる Auto Layout の情報は : IB キャンバスおよび Document Outline Control 上と Issue Navigator 上と実行時のコンソール (この章では主にコンソールの話)
  • 曖昧なレイアウトかどうかは hasAmbiguousLayout で確認できる。 exerciseAmbiguityInLayout でランダムにフレームを変更できる
  • ドキュメント化されていない便利なメソッドもある
  • 制約の衝突はコンソールログを確認。 UIViewAlertForUnsatisfiableConstraints のシンボリックブレークポイントでも捉えられる
  • 制約に Identifier を追加するのと、軸やビューごとに制約を確認する
  • ビューデバッガーが Xcode 6 で追加されてる。 制約の表示もできる

9 章 : サイズクラスとトレイトコレクション

  • トレイトコレクション。 ミュータブルなので最適な値を持つ?? みたいなことが書かれていたがミュータブルという表現がよくわからない。 (例えばアプリのウィンドウがスクリーンよりも小さい場合に、UIScreen が持つトレイトコレクションのサイズクラスは Regular だけど UIWindow が持つトレイトコレクションのサイズクラスは Compact になる、みたいなことかなーと思っている。 が、あってるかどうかは不明)
  • 画面回転
  • IB においてサイズクラスに応じたレイアウトが可能

感想

Auto Layout はもちろんのこと、view の階層構造や view のレンダリングのライフサイクル周りなどについても学ぶことができて、非常に良い本だった。

初心者ながらに 「iOS の view 周りについて必読の一冊なのではないか」 と思った。

関連