踊る犬.netブログ (旧)

[iOS] 動的なレイアウトを超手軽に実装する方法

概要

AutoLayoutが超手軽に使える、Masonryをご紹介します!

AutoLayoutで動的レイアウト、しかし…

不特定なサイズの画像や長さのテキストを取り扱う時、どうしても固定サイズのViewレイアウトでは限界があります。
コンテンツのサイズに合わせて、動的にレイアウトを調整する必要があります。
また、単にText ViewやImage Viewのサイズを変えればいいのではなく、周辺のViewも調整が必要なので結構大変です。

そんな動的レイアウトの要件に対しては、AutoLayoutがよく使用されます。
AutoLayoutは、親Viewのサイズ変更があった時に子View同士の間隔やサイズを自動で調節してくれる技術です。
そのレイアウト方法は、Constraint(制約)と呼ばれるものをViewに与える事で定義します。
AutoLayoutを使うと有効な時とその利点は以下の2つです:

  1. Interface Builderを使って、動的なサイズや配置のViewヒエラルキーを組む時
    • 親Viewのサイズ変更に合わせてレイアウトしなおす処理を追加で書く必要が無くなる
    • 例: 不特定サイズの画像を表示するTable view cell内のViewのレイアウト
  2. Interface Builderを使わないでViewを組む時
    • 位置とサイズベースではなく、制約ベースでレイアウトを定義できる
    • 例: 「親Viewに対して上下左右に10pxのマージンを設けた子Viewを配置」というセマンティクスで定義できる

2つめは、異なるスタイルで記述できるという事なんですが、利点となるには苦しい理由があります。
僕の場合はInterface Builderを使わないので、2番が利点にならなければ使う意味はほぼありません。

超めんどくさいAutoLayoutの記述

コードベースでのUI実装において、AutoLayoutが利点となるには苦しいその理由は、AutoLayoutをコードで記述すると超冗長になるという点です。
つまり、全然簡単じゃない!楽じゃない!

「親Viewに対して上下左右に10pxのマージンを設けた子Viewを配置」を実際にコードで記述した場合、以下のようになります。

#!objectivec
UIView *superview = self;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[

    //view1 constraints
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeLeft
                                multiplier:1.0
                                  constant:padding.left],   

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeBottom
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeBottom
                                multiplier:1.0
                                  constant:-padding.bottom],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeRight
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeRight
                                multiplier:1
                                  constant:-padding.right],

 ]];

はい、やってられないですね!

Masonryでお手軽AutoLayout

AutoLayoutは便利だけど、NSLayoutConstraintによる記述は前述の通り冗長で面倒です。
それが原因で、利点を感じられず個人的に今まで全く使ってきませんでした。

しかし、Masonryというライブラリがこの悩みを一気に解決してくれました。
このライブラリは、AutoLayoutの学習コストを最小限に押さえつつ、少ない記述量で使用できるようにしてくれるものです。
これは嬉しい!

前述の例「親Viewに対して上下左右に10pxのマージンを設けた子Viewを配置」をMasonryで記述すると:

#!objectivec
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

とっっっっっても簡単ですね!!
制約ベースでの記述は、HTMLとCSSのような感覚で書けるので、とても重宝しそうです。