Edited at

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


結果

左上から詰めるようなよくある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の場合

・もっとパーツ然としていたほうが良いですかね?