SnapKitライブラリを使ったレイアウト構築

株式会社アドグローブ iOSアプリエンジニアの西村です。

先日とある開発案件で初めて『SnapKit』ライブラリを使う機会がありました。
普段はInterface Builderを使い、画面レイアウトを構築していくことが多かったのですが
該当案件ではコード上でレイアウト構築をする方針だったため、このライブラリを使うことになりました。
このSnapKitがなかなか使い勝手がよかったので紹介をさせていただきます。

SnapKitについて

github.com

『SnapKit』はコード上でAutoLayoutが簡単に書けるというライブラリです。
CocoaPods, Carthageに対応しています。

早速試してみましょう。
今回は以下のように画像(ロゴ)と、その下にテキスト(会社名)を表示してみます。

準備

環境情報

MacOS 11.6
Xcode 13.1
CocoaPods: 1.10.2
SnapKit: 5.6.0

まずはSnapKitをインストールします。
今回はCocoaPodsで進めます。

PodfileにSnapKitを追記し、pod install します。

pod 'SnapKit'

実践

SnapKitをimportしましょう

import SnapKit

これでSnapKitが使えるようになりました。
まずはロゴを表示するUIImageViewとテキストを表示するUILabelを定義します。

let logoImageView = UIImageView(image: UIImage(named: "logo"))
let textLabel = UILabel()
textLabel.text = "株式会社アドグローブ"

ここからSnapKitの出番です。
レイアウトを調整していきましょう。
定義したImageViewとLabelをViewに追加します。

view.addSubview(logoImageView)
view.addSubview(textLabel)

一度この状態でビルドしてみます。

位置もサイズも指定していないので画像の位置は上にあり、ラベルは見えてもいません。
では、SnapKitを使い調整していきましょう。

まずは画像から調整します。

logoImageView.snp.makeConstraints { make in
    make.top.equalToSuperview().offset(32)
    make.leading.equalToSuperview().offset(10)
    make.width.equalTo(220)
    make.height.equalTo(36)
}

簡単に説明します。

logoImageView.snp.makeConstraints { make in
    // 画像のy座標を親のViewのy座標と揃え、32pt下げる
    make.top.equalToSuperview().offset(32)
    // 画像のx座標を親のViewのx座標と揃え、10pt右にずらす
    make.leading.equalToSuperview().offset(10)
    // 画像の幅を220ptにする
    make.width.equalTo(220)
    // 画像の高さを36ptにする
    make.height.equalTo(36)
}

つまり、画像の幅と高さは220x36に設定し、親Viewの(0, 0)座標から(36, 10)ずらした位置になります。
これでビルドしてみます。

お、ちょうどいい位置になったのではないでしょうか?
ではテキストも調整してみましょう。

textLabel.snp.makeConstraints { make in
    make.top.equalTo(logoImageView.snp.bottom).offset(20)
    make.leading.equalTo(logoImageView.snp.leading)
}

書き方としては同じです。
親のViewからだけではなく、定義したViewからの調整も出来ます。

textLabel.snp.makeConstraints { make in
    // テキストラベルのy座標はロゴ画像の最下部(bottom)と揃え、20pt下にずらす
    make.top.equalTo(logoImageView.snp.bottom).offset(20)
    // テキストラベルのx座標をロゴ画像のx座標と揃える
    make.leading.equalTo(logoImageView.snp.leading)
}

ビルドしてみましょう。

できました。簡単ですね!

注意点

SnapKitを使う上での注意点をいくつか紹介したいと思います。

・addSubview前にレイアウトを設定するとクラッシュする
 レイアウト設定は設定したいViewが追加(addSubview)されている必要があります。

・AutoLayoutのエラーはログを見る必要がある
 例えばですが、以下のようにレイアウトを設定します。

textLabel.snp.makeConstraints { make in
    make.top.equalTo(logoImageView.snp.bottom).offset(20)
    make.bottom.equalTo(logoImageView.snp.top).offset(-10)
    make.leading.equalTo(logoImageView.snp.leading)
}

テキストラベルのtopは画像のbottomから20pt下だが、bottom画像のtopより10pt上と設定しています。
何かおかしいですね?
矛盾があった場合、IntefaceBuilderではエラーや警告が表示されますが、SnapKitでのレイアウト調整はビルドしてみないとわかりません。
その場合はログに以下のようなAutoLayoutエラーが表示されますので内容を確認して対応しましょう。

SnapKitSample[2529:1147362] [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. 
(
    "<SnapKit.LayoutConstraint:0x600000ae4780@ViewController.swift#28 UIImageView:0x7ff17e20fe20.height == 36.0>",
    "<SnapKit.LayoutConstraint:0x600000ae4420@ViewController.swift#32 UILabel:0x7ff17e210800.top == UIImageView:0x7ff17e20fe20.bottom + 20.0>",
    "<SnapKit.LayoutConstraint:0x600000ae4660@ViewController.swift#33 UILabel:0x7ff17e210800.bottom == UIImageView:0x7ff17e20fe20.top + 10.0>"
)

最後に

個人的にNSLayoutConstraintやNSLayoutAnchorを使ったコードでのレイアウト調整よりInterfaceBuilderを使ったレイアウト作成の方がわかりやすく簡単だなと思っていましたが、
今回SnapKitを使ってみて「これはInterfaceBuilderより楽で便利かもしれない…!」と感じました。

みなさんもSnapKitが気になったらぜひ試してみてくださいね!