SnapKit を使いこなして楽チン AutoLayout
こんにちは。テクノロジーチームの @tdkn (てどこん) です。
最近は iOS アプリの開発を担当させてもらっていて Swift 3 を書きまくる日々を過ごしています。
ネイティブアプリの開発スキルが上昇中。ありがたいことです!
さて、今回は iOS のアプリを開発して行く上で、
「レイアウトもコードベースで書きたい派」のあなたに超絶便利かもしれない
SnapKit の使い方を紹介したいと思います。
Interface Builder:question:何それ、美味しいの:question::satisfied:
SnapKit って何?
皆さんは iOS のユーザーインターフェースをどのように構築していますか?
View の frame
に対して、寸法や位置を頑張って計算して直接値を設定していますか?
AutoLayout を使えば、View 階層に属する各 View について、
それぞれに課された制約 (Constraint) を満たすよう、寸法や位置を動的に計算します。
これらの寸法や位置の調整などは制約にもとづいてシステムが自動的に行います。
AutoLayout については、Apple の公式日本語ドキュメント「Auto Layout ガイド」が詳しいのでぜひそちらをご覧になってみてください。
SnapKit は AutoLayout の DSL です。
Interface Builder を使わずに AutoLayout をコードで実現しようとすると、
NSLayoutConstraint
を使う必要があるわけですが、普通に NSLayoutConstraint
を書こうとすると
引数たっぷりのコンストラクタを使わなければならず、その冗長さが非常につらい...。
たとえば、縦横中央揃えに
100pt×100pt
のラベルを置きたい場合
[![screenshot-00][screenshot-00]][screenshot-00]{:.no-border target="_blank"}
NSLayoutConstraint | SnapKit |
---|---|
{% gist 97f11e23dff03586b933c93e4d9b422f %} | {% gist 1276bde3dfa4c1094b52c3ec59a56179 %} |
{: .gist } |
見て下さいこの記述量の差!!
NSLayoutConstraint
の煩わしさを解決するために、
SnapKit は「シンプルで拡張性のある記法」・「メソッドチェーン」・「補完によるタイプセーフコーディング」をコンセプトに設計されています。
そんなこんなで、使わない手は無いというわけです!
使い方
基本を押さえれば、なんてことはないです!
慣れてくると、「こう書けばあんな風に配置されるよな」というのが頭の中でイメージ出来るようになってきます。
習うより慣れろということで、さっそく手を動かしてみましょう!
まずはこのスクリーンショットのような画面を作成してみることにします。
Sample-01
[![Sample-01][screenshot-01]][screenshot-01]{:.no-border target="_blank"}
なんか UILabel
がたくさんありますね。どうやって配置しているのか1つずつ見ていきましょう。
- View を生成する:
let label = UILabel()
- 親の View に追加する:
addSubView(label)
- 制約を付加する:
label.snp.makeConstraints()
1 と 2 は iOS 開発をしていくうえで基本中の基本なので説明を省略しますが、
記事の最後の方でサンプルコードへのリンクを掲載していますので見てみて下さい。
今回重要なのは 3 なのでこちらをメインに解説していきます!
Center
labelCenter.snp.makeConstraints({ (make) in
make.width.equalTo(100)
make.height.equalTo(100)
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
})
- 幅
width
を100pt
にする - 高さ
height
を100pt
にする - X軸方向の中心位置
centerX
をsuperview
と揃える - Y軸方向の中心位置
centerY
をsuperview
と揃える
ポイント
- ここで言う
superview
は、UIViewController
がもともと持っているview
のことを指しており、
UILabel
などは全てそこに.addSubview()
している状態です
Top
labelTop.snp.makeConstraints({ (make) in
make.width.height.equalTo(100)
make.bottom.equalTo(labelCenter.snp.top).offset(-10)
make.centerX.equalToSuperview()
})
- 幅と高さ
width.height
を100pt
にする - 下端
bottom
を labelCenter の 上端top
と揃えて、-10pt
ずらす - X軸方向の中心位置
centerX
をsuperview
と揃える
ポイント
- labelCenter では個別に指定していた
width
とheight
はチェインによりまとめて指定することができる.equalTo()
メソッドには ViewAttribute を指定することができる (.snp.top
,.snp.right
,.snp.bottom
,.snp.left
など)
Right
labelRight.snp.makeConstraints({ (make) in
make.size.equalTo(100)
make.top.equalTo(labelCenter)
make.left.equalTo(labelCenter.snp.right).offset(10)
})
- 大きさ
size
を100pt
×100pt
にする - 上端
top
を labelCenter と揃える - 左端
left
を labelCenter の左端right
と揃えて10pt
ずらす
ポイント
width
とheight
はsize
と省略することができる- 下の書き方は意味的には同じ
make.top.equalTo(labelCenter.snp.top)
make.top.equalTo(labelCenter)
Bottom
labelBottom.snp.makeConstraints({ (make) in
make.size.equalTo(labelCenter)
make.top.equalTo(labelCenter.snp.bottom).offset(10)
make.centerX.equalToSuperview()
})
- 大きさ
size
を labelCenter と揃える - 上端
top
を labelCenter の下端bottom
と揃えて10pt
ずらす - X軸方向の中心位置
centerX
をsuperview
と揃える
Left
labelLeft.snp.makeConstraints({ (make) in
make.size.equalTo(labelCenter)
make.top.equalTo(labelCenter)
make.right.equalTo(labelCenter.snp.left).offset(-10)
})
- 大きさ
size
を labelCenter と揃える - 上端
top
を labelCenter と揃える - 右端
right
を labelCenter の左端left
と揃えて-10pt
ずらす
ふぅ...
コードを見てなんとなくコツが分かってきたのではないでしょうか?
基本的には 「A と B の幅を同じにする」とか「C の左端と D の右端をくっ付ける」などのように、
View 同士の関係性を記述していくような使い方をします。
SnapKit の便利なメソッド
では、ここらで SnapKit に用意されている便利なメソッドを紹介していこうかと思います :relaxed:
public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> SnapKit.ConstraintMakerEditable
public func dividedBy(_ amount: ConstraintMultiplierTarget) -> SnapKit.ConstraintMakerEditable
public func offset(_ amount: ConstraintOffsetTarget) -> SnapKit.ConstraintMakerEditable
public func inset(_ amount: ConstraintInsetTarget) -> SnapKit.ConstraintMakerEditable
multipliedBy
- 倍数を指定して掛け合わせます。半分なら
multipliedBy(0.5)
、倍ならmultipliedBy(2.0)
など。
- 倍数を指定して掛け合わせます。半分なら
dividedBy
- 指定した数値で割ります。半分なら
dividedBy(2)
など。
- 指定した数値で割ります。半分なら
offset
- 最初のサンプルで出てきたのでお分かりかと思いますが、位置をずらすことができます。
inset
- 内側の余白を開けることができます。CSS で例えると
padding
のような感覚でしょうか。
- 内側の余白を開けることができます。CSS で例えると
Constraints は内部で連立一次方程式として扱われていることを知っておくとさらに理解が深まると思います。
View Formula
[![view-formula][view-formula]][view-formula]{:.no-border target="_blank"}
そして、これらの便利メソッドを駆使して作った画面がこちら!
Sample-02
[![Sample-02][screenshot-02]][screenshot-02]{:.no-border target="_blank"}
謎にハイカラだけど気にしなくておk。
白い部分は inset
を使って各辺に 30pt
の余白を作りました。
その他の色付きの部分は multipliedBy
や dividedBy
で比率を指定して配置しています。
本記事の最後にサンプルコード用の GitHub Repository を掲載していますので、Clone してぜひ配置方法を読み解いてみてください!
レイアウトのデバッグ方法
Xcode には View UI Hierarchy
という機能があります。
これを使うと、View に対してどのような Constraints が適用されているかを見ることができたり(左ペイン)、
実際に描画されているサイズや、使用されているフォントを調べることができたりします(右ペイン)。
View UI Hierarchy
[![View UI Hierarchy][screenshot-03]][screenshot-03]{:.no-border target="_blank"}
AutoLayout を使ってコードでレイアウトをして行く中で、
シミュレーターや実機でうまく配置ができずに行き詰まった場合は、
まずこの View UI Hierarchy
を使って原因を探りましょう。
Layout Issues
[![Layout Issues][screenshot-04]][screenshot-04]{:.no-border target="_blank"}
上のスクリーンショットは
Holizontal position is ambiguous for UILabel
(ラベルの水平位置が曖昧です) と怒られている例です。
50%
のラベルが枠外にはみ出ていたり、なんだかサイズも色々おかしいですね。
このように、AutoLayout の制約に問題があってうまく配置できない場合は Layout Issues
として警告を出してくれるのです。
サンプルコード
- 記事中で使用したサンプルのソースコードはこちら
- tdkn/EasyToUseSnapKit - GitHub{:.no-border target="_blank"}
$ git clone git@github.com:tdkn/EasyToUseSnapKit.git
〆
今回は、コーディングでレイアウトをするときに便利な SnapKit の使い方と、ちょっとしたデバッグの Tips をまとめました。
慣れるとスラスラ書けるようになるのでぜひマスターして爆速コーディングできるようになってみてはいかがでしょうか。
最後まで読んでいただきありがとうございました!
[screenshot-00]: /images/2016/12/Screen Shot 2016-11-20 at 22.33.39.png
[screenshot-01]: /images/2016/12/Screen Shot 2016-11-14 at 00.13.48.png
[screenshot-02]: /images/2016/12/Screen Shot 2016-11-18 at 01.35.33.png
[screenshot-03]: /images/2016/12/Screen Shot 2016-11-18 at 01.47.13.png
[screenshot-04]: /images/2016/12/Screen Shot 2016-11-18 at 01.59.34.png
[view-formula]: /images/2016/12/Screen Shot 2016-11-21 at 00.07.11.png