【iOS】【AutoLayout】画像サムネイル用途等のUICollectionViewのレイアウト方法

2016.07.01

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

おばんです、友人に素敵なカッフェに連れて行ってもらって、そこがすごくおしゃれで、おしゃれなところに行くにはやはりそれなりの身なりと精神が必要だなとか感じてしまった田中です。
美味しかったです。精進します。

さて、今日はちょっとしたAutoLayoutの話です。

サムネイルの実装をした時に期待するもの

例えばこんなUIの実装を求められた時、どうしますか?

スクリーンショット_2016-07-01_18_41_05

こういうUIは、例えばなにかの画像のサムネイルとして要素を配置して、その領域がスクロールする、なんてものをよく見かけるかと思われます。
おそらくUICollectionViewをAutoLayout使ってStoryboardで配置して、下詰めマージン0左右マージン0として中のセル要素はUICollectionViewFlowLayoutでマージンとサイズを決める、などの実装をするのではないでしょうか。

さて、では今の実装で「サムネイルの要素数が少なかった場合」はどうなるでしょうか?

スクリーンショット_2016-07-01_18_49_19

うーむ。
なにかコレジャナイ感。
きっと期待するのは以下のようなものではないでしょうか。

スクリーンショット_2016-07-01_18_46_05

サムネイルの要素数が少ない時は要素をセンター寄せにする、というものではないでしょうか。
UICollectionViewFlowLayoutで実装する方法を僕は考え付かなかったのですが、ではどうしようか、これの実装方法を紹介します。

要素のセンター寄せの制御

UICollectionViewFlowLayoutで実装する方法は思いつかない。
とはいえ、要素数に合わせてパターンを考えてIBを作るというのもナンセンスです。
以下のように考えてみました。

スクリーンショット_2016-07-02_10_40_02

簡単に言うとこんなAutoLayoutのかけ方をして、マージン用UIViewのWidthのNSLayoutConstraintのconstantの値を操作してUICollectionViewがセンター寄せになるように配置しようという目論見。

計算方法は単純な引き算です。

スクリーンショット_2016-07-01_19_03_40_2

①:画面の横幅サイズ
②:UICollectionViewの要素と横幅マージンを足したサイズ
これで①-②をすると余りとなる横幅が出るので、その余りを2で割った値を左右それぞれのView-Widthのconstantの値とすればUICollectionViewがセンターに寄ります。

そんなに書くまでもなく簡単で、仮計算のものですがレイアウト計算するソースは例えば以下の通りです。

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var leftMarginViewWidth: NSLayoutConstraint!
    @IBOutlet weak var rightMarginViewWidth: NSLayoutConstraint!
    
    @IBOutlet weak var leftMarginView: UIView!
    @IBOutlet weak var rightMarginView: UIView!
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    var items: [Item] = [Item(), Item(), Item()] {
        didSet {
            adjustCollectionLayout()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        leftMarginView.backgroundColor = UIColor.lightGrayColor()
        rightMarginView.backgroundColor = UIColor.lightGrayColor()
        collectionView.backgroundColor = UIColor.orangeColor()
        
        adjustCollectionLayout()
    }
    
    func calculateCollectionWidth() -> CGFloat {
        if items.count > 0 {
            // お好みのUICollectionViewFlowLayoutの配置分の計算をしてください
            // ここでは仮に、要素数 * 要素一つの横幅60point
            return CGFloat(items.count * 60)
        }
        return 0
    }
    
    func calculateMarginViewWidth() -> CGFloat {
        // collectionViewの横幅が親viewの横幅より値が小さかった場合のみセンター寄せさせる作り
        if calculateCollectionWidth() < view.frame.size.width {
            return (view.frame.size.width - calculateCollectionWidth()) / 2
        }
        return 0
    }
    
    func adjustCollectionLayout() {
        leftMarginViewWidth.constant = calculateMarginViewWidth()
        rightMarginViewWidth.constant = calculateMarginViewWidth()
    }
    
}

これで完了です!

スクリーンショット 2016-07-01 20.18.49

サンプルをGitHubの方にあげておきましたので、見てみたい方はどうぞ。
サンプルではマージン用のViewの背景色はlightGrayColor()にしていますが、collectionViewと同じ色にすれば領域としては同じような感覚かと思いますので実際にはそうしましょう。

まとめ

自分もつい最近少しAutoLayoutと仲良くなってきた感じがあるので、こんなこと出来るようになってきました。
レイアウトの組み方はたくさんありますので、やり方は紹介した限りではまったくありませんが一つの方法としてNSLayoutConstraintをIBOutletでつなぎ、constantプロパティの値を操作してあげるという実装は結構アリなのかなと思いましたのでぜひお役立てください。?