LoginSignup
4

More than 5 years have passed since last update.

[iOS]タグ一覧っぽい左上詰めのUICollectionView

Last updated at Posted at 2018-10-27

結果

左上から詰めるようなよくあるViewですが、レイアウトが無いので自分で組まなきゃいけないという、個人的に萎えるパーツの代表格です。(確かAndroidなら割と簡単ですよね)
Simulator Screen Shot

私はこれまで3回組んでいますが、もういい加減このViewに時間を掛けるのがいやになったのでパーツ化しました。(たぶんどこかで既出だと思いますが)

コードはこちら
https://github.com/osanaikoutarou/TagsViewSample

もちろんdidSelectItemAtIndexPathで選択可能です

どのようにパーツ化するか

cellSizesと、horizontalMarginと、verticalMargin、そして全体の枠であるinsetを、カスタムLayoutに設定します。


let layout = TagsCollectionViewLayout()

// 呼び出し
layout.setup(cellSizes: cellSizes,
             horizontalMargin: 10,
             verticalMargin: 10,
             collectionViewInset: UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15))

collectionView.setCollectionViewLayout(layout, animated: false)

中身はゴニョゴニョ計算しています

/// レイアウトを指定,左上詰めで計算する
///
/// - Parameters:
///   - cellSizes:左上から順のサイズ
///   - horizontalMargin: cell同士のmargin 上下
///   - verticalMargin: cell同士のmargin 左右
///   - collectionViewInset: 全体のInset
func setup(cellSizes:[CGSize], horizontalMargin:CGFloat, verticalMargin:CGFloat, collectionViewInset:UIEdgeInsets) {
    self.cellSizes = cellSizes
    self.horizontalMargin = horizontalMargin
    self.verticalMargin = verticalMargin
    self.collectionViewInset = collectionViewInset

    // 左上から並べていきます

    var currentPoint:CGPoint = CGPoint(x: collectionViewInset.top, y: collectionViewInset.left)
    var currentRowHeight:CGFloat = 1

    for (i, size) in cellSizes.enumerated() {
        let indexPath = IndexPath(row: i, section: 0)

        if (currentPoint.x + size.width <= collectionViewWidth - collectionViewInset.left - collectionViewInset.right) {
            // 収まる
            let frame = CGRect(x: currentPoint.x, y: currentPoint.y, width: size.width, height: size.height)
            cellFrames[indexPath] = frame

            if (currentRowHeight < size.height) {
                currentRowHeight = size.height
            }
            currentPoint.x += size.width + horizontalMargin
        }
        else {
            // 改行したうえで
            currentPoint.x = collectionViewInset.left
            currentPoint.y += currentRowHeight + verticalMargin
            currentRowHeight = 1

            if (currentPoint.x + size.width <= collectionViewWidth - collectionViewInset.left - collectionViewInset.right) {
                // 収まる
                let frame = CGRect(x: currentPoint.x, y: currentPoint.y, width: size.width, height: size.height)
                cellFrames[indexPath] = frame

                if (currentRowHeight < size.height) {
                    currentRowHeight = size.height
                }
                currentPoint.x += size.width + horizontalMargin
            }
            else {
                // 収まらない
                currentPoint = CGPoint(x: 0, y: currentPoint.y + verticalMargin + currentRowHeight)
                currentRowHeight = 1

                let frame = CGRect(x: currentPoint.x,
                                   y: currentPoint.y,
                                   width: collectionViewWidth,
                                   height: size.height)
                cellFrames[indexPath] = frame

                if (currentRowHeight < size.height) {
                    currentRowHeight = size.height
                }
                currentPoint.x = collectionViewInset.left
            }
        }
    }
}

任意の位置にセルを配置するための方法については、毎度こちらを参考にしています。
ありがとうございます。
https://dev.classmethod.jp/smartphone/iphone/ios6-uicollectionview-customlayout/

cellSizesには何を渡すか

横幅が無制限だった際のcellSizeです
例えば長い文章だとwidthが1000などになるかもしれません。そのまま渡せば良いように計算します

積み残し TODO

・改行処理(2行で表示したいなど)
・複雑なUIの場合
・もっとパーツ然としていたほうが良いですかね?

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
4