検証環境:
Xcode 12.4
Swift 5.3.2
UICollectionViewCell の Self-Sizing 機能でコンテンツに基づいて動的にセルサイズを調整する方法について。
まず今回 UICollectionView を使って実現したいレイアウトは横幅が画面幅(collectionView.frame.width
)で、縦幅はセル内のコンテンツが可変であるためそれが収まるように動的に決まり、縦スクロールする UITableView のようなレイアウト。
セルサイズを自分で計算するのではなく、 Self-Sizing 機能を使って自動で調整されるようにしたい。
StoaryBoard 設定
StoryBoard で Root View に UICollectionView 及び UICollectionViewCell のビューを置く。
UICollectionView と UICollectionViewDelegateFlowLayout は @IBOutlet でコード上の定義と接続する。UICollectionViewCell の ContentView には
Lines 0
の UILabel を置く。
UILabel に ContentView の4辺と合わせる制約と、暫定のwidth
の制約を付ける。
そして UILabel と UILabel のwidth
制約は @IBOutlet でコード上の定義maxWidthConstraint
と接続する。
コードサンプル
セルフサイジングを有効にするにはUICollectionViewFlowLayout.estimatedItemSize
に 0 以外の値、通常はUICollectionViewFlowLayout.automaticSize
を設定する必要がある。
UICollectionViewFlowLayout.itemSize
やUICollectionViewDelegateFlowLayout#collectionView(_:layout:sizeForItemAt:)
デリゲートメソッドによるセルサイズ指定は不要。
import UIKit final class ViewController: UIViewController { var items: [String] = [] override func viewDidLoad() { super.viewDidLoad() for _ in 0..<10 { items.append(String(repeating: "あいうえおかきくけこ", count: .random(in: 1...10))) } } @IBOutlet weak var collectionView: UICollectionView! { didSet { collectionView.dataSource = self collectionView.delegate = self collectionView.alwaysBounceVertical = true } } @IBOutlet weak var flowLayout: UICollectionViewFlowLayout! { didSet { flowLayout.minimumLineSpacing = 8 flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize flowLayout.scrollDirection = .vertical flowLayout.sectionInset = .init(top: 8, left: 0, bottom: 8, right: 0) } } } extension ViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell cell.contentView.backgroundColor = (indexPath.item % 2 == 0) ? .systemGray4 : .systemGray6 let item = items[indexPath.item] cell.titleLabel.text = item cell.maxWidth = collectionView.bounds.width - 16 return cell } } extension ViewController: UICollectionViewDelegate { } class CollectionViewCell: UICollectionViewCell { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var maxWidthConstraint: NSLayoutConstraint! var maxWidth: CGFloat? { didSet { guard let maxWidth = maxWidth else { return } maxWidthConstraint.constant = maxWidth } } override func awakeFromNib() { super.awakeFromNib() // iOS12のみ以下制約をつけないとAutoLayoutが効かない contentView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ contentView.leftAnchor.constraint(equalTo: leftAnchor), contentView.rightAnchor.constraint(equalTo: rightAnchor), contentView.topAnchor.constraint(equalTo: topAnchor), contentView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } }