LoginSignup
32
20

More than 3 years have passed since last update.

逆引きSnapKit - AutoLayoutをコードからサクッと設定しよう

Last updated at Posted at 2020-07-22

はじめに

SwiftUIの登場でAutoLayoutの出番は少なくなったものの、業務ではまだまだAutoLayoutを使用する機会も多いのではないでしょうか?

AutoLayoutと言えばStoryboardを思い浮かべる人も多いと思います。しかし、もっさりした動作のStoryboard(Interface builder)をわざわざ開かなくとも、AutoLayoutを設定することができます。SnapKitならめちゃめちゃ簡単に!

SnapKitを使ったサンプルコード

view1とview2を同じ大きさにして左揃えで縦に並べる例

SnapKitなし

guard let superview = view1.superview else {
    return
}
view1.translatesAutoresizingMaskIntoConstraints = false
view2.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    view1.widthAnchor.constraint(equalToConstant: 200),
    view1.heightAnchor.constraint(equalToConstant: 50),
    view1.topAnchor.constraint(equalTo: superview.topAnchor, constant: 50),
    view1.leftAnchor.constraint(equalTo: superview.leftAnchor, constant: 50),
    view2.widthAnchor.constraint(equalTo: view1.widthAnchor),
    view2.heightAnchor.constraint(equalTo: view1.heightAnchor),
    view2.leftAnchor.constraint(equalTo: view1.leftAnchor),
    view2.topAnchor.constraint(equalTo: view1.bottomAnchor, constant: 16)
])

SnapKitあり

view1.snp.makeConstraints { make in
  make.size.equalTo(CGSize(width: 200, height: 50))
  make.top.left.equalTo(50)
}
view2.snp.makeConstraints { make in
  make.left.size.equalTo(view2)
  make.top.equalTo(view2.snp.bottom).offset(16)
}

いかがでしょうか?SnapKitを使うことで書き味や可読性が、グッと向上していることがおわかりになると思います。ちなみに2020.07現在、SnapKitはGitHubで1万6千スターを獲得しています。(Storyboard嫌いの多さにビックリ!)

本記事では、SnapKitの使い方を逆引きリファレンス形式で紹介していきます。

動作確認環境

  • Xcode 11.6
  • Swift 5
  • SnapKit 5.0.1

まずは基本から

makeConstraintsメソッドのクロージャ内で制約を設定する

// SnapKitをインポートすると、UIViewにsnpというプロパティが生える
import SnapKit

// snpプロパティのmakeConstraintsメソッドの引数のクロージャ内で、制約を設定する
view.snp.makeConstraints { make in
    // view.top = otherView.top
    make.top.equalTo(otherView.snp.top)
    // view.left = otherView.right + 8
    make.left.equalTo(otherView.snp.right).offset(8)
}

これ以降、make変数が出てきた場合は、makeConstraintsのクロージャ内での記述です。

同名のプロパティに対する制約を設定するときは、引数にUIView/UILayoutGuideを渡すことができる

// make.top.equalTo(otherView.snp.top)と同じ
make.top.equalTo(otherView)

複数のプロパティをチェーンで設定できる

// make.top.equalTo(otherView); make.bottom.equalTo(otherView)と同じ
make.top.bottom.equalTo(otherView)

親viewに対する制約のときは専用のメソッドが用意されている

make.top.equalToSuperview()
make.top.lessThanOrEqualToSuperview()
make.top.greaterThanOrEqualToSuperview()

幅と高さには定数を指定できる

// width = 100
make.width.equalTo(100)
// height <= 200
make.height.lessThanOrEqualTo(200)
// width >= 400 && height >= 400
make.size.greaterThanOrEqualTo(CGSize(width: 400, height: 400))

幅と高さ以外に定数を指定すると、親ビューに対する制約になる

// left <= superview.left + 10
// make.left.lessThanOrEqualToSuperview().offset(10)と同じ
make.left.lessThanOrEqualTo(10)

逆引きリファレンス

制約の優先度を設定する

make.top.equalTo(otherView).priority(600)
make.top.equalTo(otherView).priority(.low)      // = 250
make.top.equalTo(otherView).priority(.medium)   // = 500(OSXの場合は501)
make.top.equalTo(otherView).priority(.high)     // = 750
make.top.equalTo(otherView).priority(.required) // = 1000

制約にラベルを付ける

AutoLayoutの警告ログやDebug View Hierarchy画面に表示されます

make.top.equalTo(otherView).labeled("viewTopConstraintToOtherView")

上下左右(top/bottom/left/right)の制約をまとめて設定する

// make.top.bottom.left.right.equalTo(otherView)と同じ
make.edges.equalTo(otherView)

// Insetsを指定する場合
// ConstraintInsetsはUIEdgeInsetsのaliasなので、UIEdgeInsetsも使用可
make.edges.equalTo(otherView).inset(ConstraintInsets(top: 8, left: 16, bottom: 8, right: 16))

// 親ビューに対するinsetsなら直接渡せる
// make.edges.equalToSuperView().inset(...)と同じ
make.edges.equalTo(UIEdgeInsets(top: 32, left: 12, bottom: 32, right: 12))

幅と高さに相対的な制約を設定する

// width = otherView.width * 2 + 50
make.width.equalTo(otherView).multipliedBy(2).offset(50)
// height = superview.height / 10
make.height.equalToSuperview().dividedBy(10)

サイズ(幅と高さ)の制約をまとめて設定する

make.size.equalTo(CGSize(width: 100, height: 100)) 

// width = otherView.width - 50, height = otherView.height - 50
make.size.equalTo(otherView).offset(-50)

中央の制約を設定する

// centerX = otherView.centerX + 10
make.centerX.equalTo(otherView).offset(10)
make.centerY.equalTo(otherView).offset(10)

// 上の2つと同じ
make.center.equalTo(otherView).offset(10)

SafeAreaに対する制約を設定する

guard let guide = view.rootSafeAreaLayoutGuide else {
    return
}
view.snp.makeConstraints { make in
    make.edges.equalTo(guide)
}

...
extension UIView {
    var rootSafeAreaLayoutGuide: UILayoutGuide? {
        var rootView: UIView? = self
        while rootView?.superview != nil {
            rootView = rootView?.superview
        }
        return rootView?.safeAreaLayoutGuide
    }
}

Widthを割合で指定しつつ、最大値を設定する

// 1. 親ビューの幅の半分に設定する
make.width.equalToSuperview().dividedBy(2).priority(.high)
// 2. 1.の制約より優先度を上げて最大値を指定する
make.width.lessThanOrEqualTo(100).priority(.required)

アスペクト比を保ったまま、親ビューの中で最大限大きく表示する

// width : height = 2 : 1のアスペクト比に設定する
make.width.equalTo(inner.snp.height).multipliedBy(2).priority(.required)
// 親ビューのサイズを超えないように設定する
make.size.lessThanOrEqualToSuperview().priority(.required)
// 上記より優先度を下げて、幅と高さを親ビューと同じに設定する
make.width.equalToSuperview().priority(.high)
make.height.equalToSuperview().priority(.high)

// 中央に表示する
make.center.equalToSuperview()

制約の参照を保持する

let topConstraint: Constraint
view.snp.makeConstraints { make in
    // constraintプロパティを呼び出す。この制約は自動的にactivateされる
    topConstraint = make.top.equalToSuperview().offset(8).constraint
}

// 制約をdeactivate
topConstraint.deactivate()

// 制約をactivate
topConstraint.activate()   

// Offsetを更新
topConstraint.update(offset: 16)

アニメーションに制約を使う

let heightConstraint: Constraint

view.snp.makeConstraints { make in
    heightConstraint = make.height.equalTo(100).offset(0).constraint
}

UIView.animate(withDuration: 1, animations: {
    self.heightConstraint?.update(offset: 200)
    self.layoutIfNeeded()
})

UIView#updateConstraints, UIViewController#updateViewConstraints内で使用する

// updateConstraintsメソッドは制約のconstantだけを更新する
view.snp.updateConstraints { make in
    make.height.equalTo(self.height)
}

既存の制約を破棄する

view.snp.removeConstraints()

既存の制約を破棄して、新しい制約を設定する

@objc func changeHeight(sender: UIButton) {
    // remakeConstraintsメソッドは既存の制約を破棄するとともに、新しい制約を設定する
    snp.remakeConstraints { make in
        make.height.equalTo(sender.tag)
        make.top.left.equalToSuperview()
        make.width.equalTo(100)
    }
}

参考

SnapKit Doc

32
20
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
32
20