SnapKitでiOSアプリのAuto Layout制約をコードで書く方法を徹底解説
Xcodeでアプリ開発を始めた頃、Storyboardはまるで魔法のように感じていました。サブビューをドラッグ&ドロップで配置できる手軽さゆえに、StoryboardなしでどうやってUIを構築するのか不思議で仕方ありませんでした。
しかし「一人前のプログラマーになるには」という思いから、ビューをコードで構築する方法を学ぶことに決めました。ところが問題が一つ。コードで制約(Constraint)を書くのは非常に面倒なのです。
let horizontalConstraint = NSLayoutConstraint(
item: newView,
attribute: NSLayoutAttribute.centerX,
relatedBy: NSLayoutRelation.equal,
toItem: view,
attribute: NSLayoutAttribute.centerX,
multiplier: 1,
constant: 0
)これはサブビューをスーパービューのX軸方向の中央に配置するための水平方向の制約です。読みにくいうえに、すべての制約をこの形式で書くのはすぐに飽きてしまいます。それでもStoryboardを使わずに開発したかった私は、代替手段を探し始めました。そこで見つけたのがSnapKitです。
SnapKitは明快で簡潔なAPIを備えており、コードでの制約記述を格段に楽にしてくれます。この記事では、SnapKitでできることの基本をいくつかの例とともに紹介します。
この記事で扱う内容
- サブビューをスーパービュー内に配置する
- サブビュー同士の相対的なレイアウト
- 制約の更新とアニメーション
はじめに:SnapKitの導入
まずプロジェクトにSnapKitをインストールしましょう。サードパーティ製ライブラリの管理にはCocoaPodsを使用します。
Podfileにpod 'SnapKit'を追加してpod installを実行します。その後、SnapKitを使いたいファイルの先頭でimport SnapKitと記述すれば準備完了です。
サブビューをスーパービュー内に配置する
まずは、サブビューをスーパービューの端に固定(ピン留め)してみます。
let subview = UIView()
view.addSubview(subview)subview.snp.makeConstraints { (make) in
make.top.equalTo(view)
make.bottom.equalTo(view)
make.left.equalTo(view)
make.right.equalTo(view)
}これにより、サブビューの上下左右の端が、スーパービューの対応する端に対して定数0で固定されます。

注目すべきは、制約を設定する前にサブビューをスーパービューに追加している点です。まだスーパービューに追加されていないビューに制約を書くと、ビューのロード時に致命的なランタイムエラーが発生します。
標準ライブラリよりはるかに読みやすいSnapKitの構文ですが、実はさらに短くできます。SnapKitには、スーパービューの端への制約をもっと簡潔に書く方法が用意されています。
let subview = UIView()
view.addSubview(subview)subview.snp.makeConstraints { (make) in
make.top.bottom.left.right.equalTo(view)
}先ほどのコードとまったく同じ結果になりますが、4行が1行になりました。
サイズの指定と中央配置
サブビューのサイズを指定することもできます。次の例では、サブビューの幅と高さを設定し、スーパービューの中央に配置しています。
subview.snp.makeConstraints { (make) in
make.width.equalTo(200)
make.height.equalTo(200)
make.centerX.equalTo(view)
make.centerY.equalTo(view)
}
とてもシンプルな例ですが、同じ値を何度も書いていて少し冗長です。同じ値を持つ制約を設定するときは、SnapKitでは次のように制約をつなげて(チェーンして)書けます。
subview.snp.makeConstraints { (make) in
make.width.height.equalTo(200)
make.centerX.centerY.equalTo(view)
}このブロックは先ほどとまったく同じ制約を生成します。SnapKitなら、よりクリーンなコードが書けるというわけです。
サブビュー同士の相対的なレイアウト
1つのビューに複数のサブビューを追加する場合、サブビュー同士の位置関係でレイアウトしたいことがよくあります。この例では以下を行います。
subview1を追加し、スーパービューの左端に固定するsubview2を追加し、subview1の右端に固定する
let subview1 = UIView()
let subview2 = UIView()
view.addSubview(subview1)
view.addSubview(subview2)subview1.snp.makeConstraints { (make) in
make.width.height.equalTo(100)
make.left.equalTo(view)
}subview2.snp.makeConstraints { (make) in
make.width.height.equalTo(subview1)
make.left.equalTo(subview1.snp.right)
}ここまでの例では、基準となるビューの対応する制約と等しくなるように設定してきました。たとえばmake.left.equalTo(view)の場合、SnapKitはsubview1のleftをviewのleftに合わせます。特に指定しない限り、SnapKitは自動的に対応する制約をマッチさせてくれるのです。
しかしsubview2のleft制約については、subview1の右端に合わせたいところです。make.left.equalTo(subview1)と書くと、subview2の左端はsubview1の左端に揃ってしまいます。そこで、subview1.snp.rightと書くことでsubview1の右端を取得し、それに合わせます。
このようにsnpを付けることで、ビューレイアウトの任意の制約にアクセスできます。

オフセットで余白を作る
subview2がsubview1の右隣に配置できたので、今度は両者の間にスペースを作りましょう。subview2にオフセット(offset)を追加します。
subview2.snp.makeConstraints { (make) in
make.width.height.equalTo(subview1)
make.left.equalTo(subview1.snp.right).offset(50)
}オフセットは任意のequalTo()の末尾につなげることで、その制約の定数値を変更できます。これでsubview2のleft制約の定数は50になります。

制約の更新とアニメーション
自分のアプリを「次の大ヒット作」にしたいなら、ちょっとした演出は欠かせません!SnapKitを使えば、制約を簡単に更新して動的なビューを作れます。
SnapKitでの制約の更新は、新しい制約の追加とほぼ同じ書き方です。ここでは、前の例のsubview1について、left制約の定数を0から50に変更してみます。
subview1.snp.updateConstraints { (make) in
make.left.equalTo(50)
}これだけです!このコードブロックが実行されると、サブビューは新しい制約に従って更新されます。

お気づきかもしれませんが、subview1を更新しただけで、subview2の制約は更新していないのに一緒に移動しました。これはsubview2がsubview1の右端に制約されているためです。subview1が移動すると、subview2はsubview1とのleft制約を守り続けます。
制約の変更をアニメーションさせる
次に、遷移を滑らかにするためのアニメーションを追加しましょう。ビューのアニメーションを経験したことがあれば、この構文は見覚えがあるはずです。
UIView.animate(withDuration: 0.3) {
subview1.snp.updateConstraints { (make) in
make.left.equalTo(50)
}
self.view.layoutIfNeeded()
}制約の更新をアニメーションする際は、サブビューの親ビューに対してlayoutIfNeeded()を呼び出す必要があります。複数のビューを同時にアニメーションする場合は、それらの最も近い共通のスーパービューに対して呼び出してください。

updateConstraints・remakeConstraints・deactivateの使い分け
updateConstraints()は既存の制約しか更新できません。存在しない制約を更新しようとすると、致命的なランタイムエラーが発生します。
サブビューに新しい制約を追加したい場合はremakeConstraints()を使いましょう。remakeConstraints()は、そのサブビューに設定済みの既存の制約をすべて削除したうえで、新しい制約を追加できるようにします。
また、deactivate()を使えば制約を削除することもできます。以下は、制約を作成・設定したあと、deactivate()で削除する例です。
var constraint: Constraint!
let subview = UIView()subview.snp.makeConstraints { (make) in
constraint = make.height.equalTo(100).constraint
}constraint.deactivate()さらに、アニメーションブロックの中でdeactivate()を呼び出せば、制約の削除もアニメーションできます。
まとめ:Storyboardはもう不要?
これであなたも、コードで制約を書く達人です。Storyboardなんてもう過去のもの、二度と戻る理由はないですよね?

実はそうとも言い切れません。StoryboardやXIB(nib)は、依然としてビューコントローラーを最速で組み立てられる手段であり、非エンジニアと協働する場合、ベータ版を配布しなくてもアプリの見た目を伝えるのに最適な方法です。
筆者自身は両方を使い分けています。最近開発したアプリでは、SnapKitによるコードでの制約と、StoryboardやXIB上のAuto Layoutを併用し、同じビューで使い分けたこともありました。結局のところ、どちらが最適かは、構築するビューごとに判断するのが良いでしょう。
最後までお読みいただきありがとうございました!この記事が気に入ったら、Twitterでプロダクトマネジメント、エンジニアリング、デザインに関する記事を投稿しているので、ぜひフォローしてください。
-
ITプロフェッショナル必見!仕事がはかどるiOSアプリ ベスト10
アプリ開発は今やあらゆる業界で欠かせない存在となり、IT業界も例外ではありません。かつてのITエンジニアは、重いPCをどこへでも持ち運ぶか、外出中は仕事を中断するしかありませんでした。しかしスマートフォンの登場により状況は劇的に変化し、ほぼすべての業務をサポートするアプリが登場しています。本記事では、iOSデバイスを最大限に活用したいITプロフェッショナルのために、厳選したおすすめアプリをご紹介します。それでは早速見ていきましょう! 1. iNetTools iPhoneとiPadの両方に対応したユニバーサルツールです。Ping、DNSルックアップ、サーバーモニター、LANスキャンなど、ネッ
-
iOS 13でNFCタグを読み書きする方法|iPhoneでの設定手順をわかりやすく解説
近年のスマートフォンには、端末同士で情報をやり取りできる「NFC」という機能が搭載されています。NFCは「Near Field Communication(近距離無線通信)」の略称で、スマートフォンとの間で情報を送受信できる無線通信技術のことです。 この機能を使えば、製品やサービス、観光名所などに関する情報に瞬時にアクセスできます。デバイスをワイヤレスで接続するには、NFCに対応したスマートタグ、いわゆる「NFCタグ」が必要です。 本記事では、NFCタグの基本知識から、iPhone(iOS 13)を使ったNFCタグの読み書き方法まで、詳しく解説します。 NFCタグとは? NFCタグとは、NFC