[iOS] Auto Layout の制約設定を楽にしてくれるライブラリ「Cartography」を試してみた #tryswiftconf

2017.03.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

try! Swift TOKYO 2017 の Sommer Panage さんの講演「UIをSwiftyに書く」で紹介されていたライブラリ「Cartography」が気になったので試してみました。

本記事では「Cartography」の使用方法や導入方法などを紹介していきます。

検証環境

  • Xcode Version 8.2.1
  • iPhone 7, iOS 10.2.1

Cartography

Auto Layout の制約を宣言的に設定できるライブラリです。

使用方法や NSLayoutConstraint を直接使用する場合との比較などについては後述します。

導入

CocoaPods を使用して導入できます。

use_frameworks!

target 'CartographyDemos' do
  pod 'Cartography', '~> 1.0'
end

NSLayoutConstraint を直接使用する場合との比較

以下のように固定サイズの View を中央に配置するレイアウトに関して、「NSLayoutConstraint を直接使用する場合」と「Cartography を使用する場合」の実装を比較してみます。

about-swift-cartography-1-center

NSLayoutConstraint を直接使用する

NSLayoutConstraint のイニシャライザを使用する場合、制約を設定する実装は以下のようになります。

childView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(
    item: childView,
    attribute: .height,
    relatedBy: .equal,
    toItem: nil,
    attribute: .height,
    multiplier: 1.0,
    constant: 100.0
    ).isActive = true
NSLayoutConstraint(
    item: childView,
    attribute: .width,
    relatedBy: .equal,
    toItem: nil,
    attribute: .width,
    multiplier: 1.0,
    constant: 100.0
    ).isActive = true
NSLayoutConstraint(
    item: childView,
    attribute: .centerX,
    relatedBy: .equal,
    toItem: view,
    attribute: .centerX,
    multiplier: 1.0,
    constant: 0.0
    ).isActive = true
NSLayoutConstraint(
    item: childView,
    attribute: .centerY,
    relatedBy: .equal,
    toItem: view,
    attribute: .centerY,
    multiplier: 1.0,
    constant: 0.0
    ).isActive = true

Cartography を使用する

Cartography を使用する場合、constrain 関数を使用して以下のように実装できます。短くて読みやすいですね。

constrain(childView, view) { childView, view in
    childView.width  == 100
    childView.height == 100
    childView.centerX == view.centerX
    childView.centerY == view.centerY
}

その他の使用例

View のエッジに対する制約を設定する

View のエッジ同士に制約を設定したい場合に inset 関数を使用できます。

constrain(webView, view) { webView, view in
    webView.edges == inset(view.edges, 0.0, 0.0, 0.0, 0.0)
}

画面いっぱいになるように WebView を配置したい場合などに使用できます。

about-swift-cartography-2-inset

View の Align と Distribute に関する制約を設定する

複数の View の端を揃えたい場合には align 関数を、 View 間に一定のスペースを設定したい場合には distribute 関数を使用できます。

constrain(view1, view2, view3, view) { view1, view2, view3, view in
    view2.center == view.center
    view2.height == 80.0
    view2.width == 80.0
    view1.size == view2.size
    view3.size == view2.size

    // Aligns
    align(top: view1, view2, view3)

    // Distributes
    distribute(by: 20.0, horizontally: view1, view2, view3)
}

上記の実装によって、以下のスクショのレイアウトを実現できます。

about-swift-cartography-3-aligning-distributing

制約を差し替える

ConstraintGroup を使用して制約をキャプチャすることによって、制約を動的に差し替えることができます。

let constraintGroup = ConstraintGroup()

// Attach `childView` to the middle left corner of its superview
constrain(childView, replace: constraintGroup) { childView in
    childView.centerY  == childView.superview!.centerY
    childView.left == childView.superview!.left
}

/* Later */

// Move the childView to the bottom right corner of its superview
constrain(childView, replace: constraintGroup) { childView in
    childView.bottom == childView.superview!.bottom
    childView.right  == childView.superview!.right
}

UIView.animate(withDuration: 0.5, animations: view.layoutIfNeeded)

上記の実装によって、以下のようなアニメーションを実現できます。

about-swift-cartography-4-replacing-constraints

さいごに

本記事では、Auto Layout の制約を宣言的に設定できるライブラリ「Cartography」を紹介しました。

紹介していない機能が他にもありますので、気になった方は Cartography の READMEFunctions Reference をチェックしてみてください!

本記事で扱ったサンプルコードは以下のリポジトリで公開しています。