iOS
Storyboard
Swift
SnapKit

脱Storyboardのすすめ

iOSでアプリを作るとだいたい使うことになるStoryboardですが、Storyboardを使わずコードベースでレイアウトを実装したらすごく良かったので、布教活動の一環として。

Storyboardでなくコードでレイアウトを実装するメリット、デメリット

メリット

  • リソースの管理が楽
    • コンフリクトしても修正が容易
    • 変更点がgithub上で分かりやすい
    • 設定されているpropertyがコードに集約されるので分かりやすい
  • ファイルをオープンするのが早い
    • 大きいStoryboardだと開くのに数秒待たされる
  • ABテストの条件分岐が容易
  • 流用や変更が容易

デメリット

  • ぱっと見で画面イメージがつかない
  • コード量が増える

多少のデメリットはあれどメリットがいっぱい!
画面イメージとかはコンフルに画面遷移図+スクショでも貼っておけば解決!

ライブラリ

いざAutoLayoutをNSLayoutConstraint書こうとすると記述がすごく長くなります。
なのでわかりやく、シンプルにAutoLayoutを記述できるSnapKitを使うのがオススメです。
AutoLayoutDSLの中では一番人気があります。

ちなみにCartographyも結構人気です。

実装例

932eabe3-1f68-e95c-9b1f-a7d7ceaba109.png

よく商品などの一覧に使われそうな上記のレイアウトを実装しようとなるとこうなります。

import UIKit
import SnapKit

class ViewController: UIViewController {
    let redView: UIView = {
        let label = UILabel()
        label.backgroundColor = .red
        return label
    }()

    let label1: UILabel = {
        let label = UILabel()
        label.text = "aaaaaaaaaaaaaaaaaaaaaa"
        return label
    }()

    let label2: UILabel = {
        let label = UILabel()
        label.text = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        label.numberOfLines = 0
        return label
    }()

    let bottomBorder: UIView = {
        let view = UIView()
        view.backgroundColor = .black
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        let margin = 10.0
        let redViewSize = CGSize(width: 60, height: 60)
        let borderHeight = 1

        view.addSubview(redView)
        view.addSubview(label1)
        view.addSubview(label2)
        view.addSubview(bottomBorder)

        redView.snp.makeConstraints { (make) in
            make.top.left.equalToSuperview().inset(margin)
            make.size.equalTo(redViewSize)
        }

        label1.snp.makeConstraints { (make) in
            make.top.equalTo(redView)
            make.left.equalTo(redView.snp.right).offset(margin)
            make.right.equalToSuperview().inset(margin)
        }

        label2.snp.makeConstraints { (make) in
            make.top.equalTo(label1.snp.bottom).offset(margin)
            make.left.equalTo(label1)
            make.right.equalTo(label1.snp.right)
        }

        bottomBorder.snp.makeConstraints { (make) in
            make.left.right.equalToSuperview()
            make.height.equalTo(borderHeight)
            make.top.greaterThanOrEqualTo(redView.snp.bottom)
            make.top.greaterThanOrEqualTo(label2.snp.bottom)
        }
    }
}

特に説明しなくてもお互いのViewがどのように関連しいるかだいたいわかると思います。
よくあるような、左右2つのViewの高さが高い方に次のViewのTopを合わせるような仕様もこのように記述することで簡単に実装できます。

bottomBorder.snp.makeConstraints { (make) in
    - - - - - 
    make.top.greaterThanOrEqualTo(redView.snp.bottom)
    make.top.greaterThanOrEqualTo(label2.snp.bottom)
}

また同じような記述は1行でまとめて書くこともできます。

redView.snp.makeConstraints { (make) in
    make.top.equalToSuperview().inset(margin)
    make.left.equalToSuperview().inset(margin)

↓↓

redView.snp.makeConstraints { (make) in
    make.top.left.equalToSuperview().inset(margin)

また特定の条件化ではlabel1label2の位置を逆にしたいという時もif分岐を使うことで簡単に実装できます。

Viewの初期化

今までStoryboardで初期化&Propertyの設定をやっていた部分がコードにくるので、どうしてもコード量が増えて見にくくなりますが、各Viewごとにまとめてクロージャーを使って初期化することで改善されます。

let label1: UILabel = {
    let label = UILabel()
    label.text = "aaaaaaaaaaaaaaaaaaaaaa"
    return label
}()

let label2: UILabel = {
    let label = UILabel()
    label.text = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
    label.numberOfLines = 0
    return label
}()

まとめ

いままでStoryboardでAutoLayoutを実装していた方からしたらコード書くのめんどくさいと思われてしまうかもしれませんが、1ヶ月もすればそれも気にならなくなるし、変更の容易性、レビューのしやすさなどのメリットが大きすぎてStoryboardが使いたくなくなってると思います!