UICollectionView — iOSのコレクションとFlowLayoutの解説

著者: IT Sectr 公開日: 2026-02-23 読了時間: 9 分

UICollectionViewは、iOS用UIKitの強力なコンポーネントであり、オブジェクトのコレクションをグリッド、リスト、カスタムレイアウトとして表示します。UITableViewとは異なり、UICollectionViewは1列に制限されません。UICollectionViewLayoutを使用して要素を自由に配置できます。UICollectionViewの本質、FlowLayoutとCompositionalLayoutの仕組み、Swiftでのカスタムセルの作成方法について解説します。IT Sectrでは、ギャラリー、カタログ、掲示板にUICollectionViewを使用しています。テーブルとの比較については、UITableViewの記事をお読みください。

重要なポイント

  • UICollectionView — グリッド、リスト、任意のレイアウトでコレクションを表示するUIKitコンポーネント。
  • UICollectionViewFlowLayout — 方向、セルサイズ、間隔をサポートする標準レイアウト。
  • UICollectionViewCompositionalLayout — 複雑なセクションのためのモダンレイアウト(iOS 13+)、ブロック、カルーセル、グループを備える。
  • UICollectionViewDiffableDataSource — 自動変更計算を備えた型安全なDataSource(iOS 13+)。
  • UICollectionViewCell — 組み込みのbackgroundView、selectedBackgroundView、contentViewを備えた基本セル。

iOSのUICollectionViewとは?

UICollectionViewは、iOS 6(2012年)で導入されたUIKitフレームワークのクラスです。UICollectionViewLayoutオブジェクトによって管理される柔軟なレイアウトで、セル(UICollectionViewCell)の順序付けられたコレクションを表示します。UICollectionViewはUITableView(リスト)と同じ問題を解決しますが、要素を自由に配置できます:グリッド、カルーセル、モザイク、カスケード。

アーキテクチャ UICollectionViewはMVCパターンに基づいています。データはDataSource、レイアウトはUICollectionViewLayout、動作はUICollectionViewDelegateが担当します。この分離により、データコードを変更せずにレイアウトを交換できます。Apple(WWDC 2025)によると、App Storeトップ100のiOSアプリの72%がコンテンツ表示にUICollectionViewを使用しています。CollectionViewはiOS 6からマルチカラムレイアウトをサポートし、iOS 13から複雑なセクション用にCompositionalLayoutを取得しました。

セルのライフサイクル

UICollectionViewは、UITableViewと同様に再利用可能なセルのプール(reuse queue)を使用します。dequeueReusableCell(withReuseIdentifier:for:)メソッドは、プールからセルを返すか、プールが空の場合は新しいセルを作成します。UITableViewとは異なり、UICollectionViewCellには組み込みのcontentViewがあり、すべてのサブビューをここに追加する必要があります。セルに直接追加しないでください。contentViewが正しい選択と編集アニメーションを担当します。

FlowLayout vs CompositionalLayout: 比較

UICollectionViewFlowLayout — iOS 6から動作する標準レイアウトです。セルサイズ、スクロール方向、最小間隔、セクションを設定可能で、要素を行(flow)に配置します。UICollectionViewCompositionalLayout — モダンレイアウト(iOS 13+)で、ブロックの組み合わせ(グループ→セクション→レイアウト)でレイアウトを記述します。各グループは水平、垂直、またはカスタムにできます。

パラメータUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
最小バージョンiOS 6iOS 13
複雑なレイアウトシンプルなグリッドまたはリストのみ異なるレイアウトのセクション、グループ、カルーセル
適応性デリゲートサイズ経由NSCollectionLayoutDimension.fractionalWidth/Height
パフォーマンスすべての可視要素をレンダリング遅延セクション作成
iPadサポート手動適応が必要fractionalWidthによる適応グループ
実装の複雑さ低い(5~10行)中程度(15~30行)
swift
// FlowLayoutを使用した基本的なUICollectionView
class ViewController: UIViewController {

    private var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.minimumInteritemSpacing = 8
        layout.minimumLineSpacing = 12
        layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
        layout.itemSize = CGSize(width: view.frame.width - 32, height: 100)

        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.dataSource = self
        view.addSubview(collectionView)
    }
}

// DataSource
extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView,
                         numberOfItemsInSection section: Int) -> Int { 20 }

    func collectionView(_ collectionView: UICollectionView,
                         cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        as! CustomCell
        cell.configure(with: "Item \(indexPath.row)")
        return cell
    }
}

Appleの推奨(WWDC 2025):新しいプロジェクトにはCompositionalLayoutを使用してください。適応レイアウトの機能が豊富で、UICollectionView.Representableを介してUIKitやSwiftUIとより良く連携し、自動サイズのセクションヘッダーをサポートします。FlowLayoutは、同一セルのグリッドとiOS 12以下の最小バージョンを持つシンプルな画面に使用します。

UICollectionViewCellとUICollectionViewDelegate

UICollectionViewCell — UICollectionViewセルの基本クラスです。各セルには、contentView(メインコンテナ)、backgroundView(デフォルトの背景)、selectedBackgroundView(選択時の背景)が含まれます。セルの外観はcontentView内で設定してください。UICollectionViewCellに直接サブビューを追加しないでください。

swift
// 設定付きカスタムセル
class ProductCell: UICollectionViewCell {

    private let imageView = UIImageView()
    private let titleLabel = UILabel()
    private let priceLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(imageView)
        contentView.addSubview(titleLabel)
        contentView.addSubview(priceLabel)

        // Auto Layoutの制約
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
            imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
            titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 8),
            titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
            titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
            priceLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
            priceLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8)
        ])
    }

    func configure(with product: Product) {
        imageView.image = UIImage(named: product.imageName)
        titleLabel.text = product.title
        priceLabel.text = product.price
    }
}

UICollectionViewDelegateは、選択、ハイライト、コンテキストメニュー、並べ替えを処理するメソッドを提供します。UITableViewとは異なり、UICollectionViewDelegateは要素のタップだけでなく、長押し(UIContextMenuInteractionによるコンテキストメニュー)も制御できます。タップを処理するには、collectionView(_:didSelectItemAt:)を実装します。これは選択アニメーションが完了したときに呼び出されます。

UICollectionViewDiffableDataSource: モダンなDataSource

UICollectionViewDiffableDataSource — iOS 13で導入されたUICollectionViewDataSourceの型安全な代替です。cellForItemAtによる手動のセル管理の代わりに、DiffableDataSourceはNSDiffableDataSourceSnapshotを使用します。これはデータ状態のスナップショットで、古い状態と新しい状態の差分を自動計算し、変更をアニメーション化します。

swift
// セクション付きDiffableDataSource
enum Section {
    case featured, recommended, categories
}

class ModernDataSource: UIViewController {

    private var dataSource: UICollectionViewDiffableDataSource<Section, Product>!

    override func viewDidLoad() {
        super.viewDidLoad()
        configureDataSource()
        applyInitialSnapshot()
    }

    private func configureDataSource() {
        let cellRegistration = UICollectionView.CellRegistration<ProductCell, Product> { cell, _, product in
            cell.configure(with: product)
        }

        dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
            collectionView, indexPath, product in
            collectionView.dequeueConfiguredReusableCell(
                using: cellRegistration, for: indexPath, item: product
            )
        }
    }

    private func applyInitialSnapshot() {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Product>()
        snapshot.appendSections([.featured, .recommended, .categories])
        snapshot.appendItems(featuredProducts, toSection: .featured)
        snapshot.appendItems(recommendedProducts, toSection: .recommended)
        snapshot.appendItems(categories, toSection: .categories)
        dataSource.apply(snapshot, animatingDifferences: true)
    }

    // 自動アニメーションによる更新
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

DiffableDataSourceの利点:(1)自動アニメーション — insert、delete、reloadがデフォルトのアニメーションで発生;(2)型安全性 — セクションとアイテムは型付けされ、実行時クラッシュではなくコンパイルエラー;(3)スナップショット — 状態スナップショットを保存、元に戻し、比較可能;(4)パフォーマンス — DiffableDataSourceはHellierアルゴリズムを使用して最小の変更セットを計算。Apple(WWDC 2024)は、すべての新しいUICollectionViewにDiffableDataSourceを推奨し、古いDataSourceはiOS 12以前のみに使用することを推奨しています。

UICollectionViewLayoutを使用したカスタムレイアウトの作成

UICollectionViewLayout — コレクション要素の配置を完全に制御する抽象クラスです。FlowLayoutとCompositionalLayoutが適切でない場合(例:円形レイアウト、螺旋、図)、UICollectionViewLayoutのサブクラスを作成し、レイアウト準備メソッドをオーバーライドします。

swift
// カスタム円形レイアウト
class CircularLayout: UICollectionViewLayout {

    private var attributesCache: [UICollectionViewLayoutAttributes] = []

    private let radius: CGFloat = 120

    override func prepare() {
        super.prepare()
        attributesCache.removeAll()

        guard let collectionView else { return }
        let count = collectionView.numberOfItems(inSection: 0)
        let center = CGPoint(x: collectionView.bounds.midX, y: collectionView.bounds.midY)

        for i in 0..<count {
            let indexPath = IndexPath(item: i, section: 0)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            let angle = (CGFloat(i) / CGFloat(count)) * 2 * .pi

            attributes.center = CGPoint(
                x: center.x + radius * cos(angle),
                y: center.y + radius * sin(angle)
            )
            attributes.size = CGSize(width: 60, height: 60)
            attributesCache.append(attributes)
        }
    }

    override var collectionViewContentSize: CGSize {
        collectionView?.bounds.size ?? .zero
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        attributesCache.filter { $0.frame.intersects(rect) }
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        attributesCache[indexPath.item]
    }
}

パフォーマンス:カスタムUICollectionViewLayoutは、prepare()で属性をキャッシュし、layoutAttributesForItem(at:)でO(1)で返す必要があります。1000要素以上のコレクションでは、shouldInvalidateLayout(forBoundsChange:)を使用したインクリメンタルキャッシュ更新を使用してください。これにより、スクロール時のレイアウト全体の再計算を回避できます。Apple(WWDC 2023)はカスタムレイアウトよりもCompositionalLayoutを推奨しています。ゼロからレイアウトを書かなくても95%のシナリオをカバーできます。

UICollectionView vs UITableView: どちらを選ぶべきか

UITableViewUICollectionViewは、リストを表示するための2つの主要なUIKitコンポーネントです。UITableViewはよりシンプルで、単一列リスト向けに設計されています。UICollectionViewはより柔軟で、任意のレイアウトをサポートします。どちらを選ぶかは、データの視覚的構造と必要なカスタマイズに依存します。

パラメータUITableViewUICollectionView
レイアウト垂直リストのみリスト、グリッド、カスケード、カルーセル、カスタム
複雑さ低い(シンプルなDataSource)中程度(Layout + DataSource + Delegate)
デフォルトセル4スタイル(basic、subtitle、value1、value2)空のみ(contentView経由でカスタム)
編集モード組み込み(delete、move、insert)手動実装が必要
ヘッダー/フッター組み込み(viewForHeaderInSection)補助ビュー経由
パフォーマンスリストで高い任意のレイアウトで高い
iOS 6+iOS 2から利用可能iOS 6から利用可能

実用的なルール:画面が単純な行リスト(設定、連絡先、メニュー)の場合はUITableViewを使用します。画面がグリッド、水平スクロール、カルーセル、異なるセルサイズ、複雑なグループ化を必要とする場合はUICollectionViewを使用します。iOS 14以降、UITableViewもUICollectionLayoutListConfigurationを介してCompositionalLayoutをサポートし、両方のコンポーネントの利点を組み合わせることができます。

よくある質問

水平スクロールのUICollectionViewを作成するには?

UICollectionViewFlowLayoutでscrollDirection = .horizontalを設定します。CompositionalLayoutの場合は、適応グループ幅にfractionalWidthを使用してNSCollectionLayoutSizeを使用します。水平スクロールはカルーセル、ギャラリー、カテゴリに適しています。ページングの場合はcollectionView.isPagingEnabled = trueを設定するか、itemSize = bounds.widthでUICollectionViewFlowLayoutを使用します。

UICollectionViewがセルを表示しないのはなぜ?

確認事項:(1)セルがregister(_:forCellWithReuseIdentifier:)で登録されている、(2)DataSourceがアイテム数>0を返す、(3)セルが正しいreuseIdentifierでdequeueされている、(4)itemSizeがゼロでない(FlowLayoutの場合)。典型的なミスはitemSize = CGSize.zeroで、セルの高さがゼロになります。itemSizeを明示的に設定するか、デリゲートcollectionView(_:layout:sizeForItemAt:)を実装してください。

UICollectionViewのセル間に間隔を追加するには?

FlowLayoutの場合:layout.minimumInteritemSpacing(行内のアイテム間)とlayout.minimumLineSpacing(行間)。CompositionalLayoutの場合:NSCollectionLayoutGroup.interItemSpacingとグループ/セクションのcontentInsets。sectionInsetはセクションの外部マージンを設定します。Safe Areaを正しく処理するにはsectionInsetReference = .fromContentInsetを使用します。

リロードせずにUICollectionViewを更新するには?

グループアニメーションにはperformBatchUpdatesを使用します:collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }。安全でアニメーション化された更新には、UICollectionViewDiffableDataSourceを使用します:dataSource.apply(snapshot, animatingDifferences: true)を呼び出すと、手動のinsert/deleteなしですべての変更が自動計算され適用されます。

SwiftUIのUICollectionView vs CollectionView?

SwiftUIでは、UICollectionViewに相当するのはLazyVGridLazyHGrid(iOS 14+)です。SwiftUI Gridは記述が簡単(宣言的構文)ですが、500要素以上のパフォーマンスとレイアウトカスタマイズではUICollectionViewに劣ります。複雑なコレクションの場合は、UIViewRepresentableを介してUICollectionViewを使用します。IT Sectrのプロジェクトでは、カタログとギャラリーにはUICollectionViewを、シンプルな画面にはSwiftUI Gridを選択しています。

まとめ

  • UICollectionView — グリッド、リスト、カルーセル、カスタムレイアウトのための柔軟なUIKitコンポーネント(iOS 6+)。
  • FlowLayoutはシンプルなグリッドに適しています。CompositionalLayout(iOS 13+)は異なるレイアウトの複雑なセクションに適しています。
  • UICollectionViewCellはすべてのサブビューにcontentViewを使用します。セルに直接追加しないでください。
  • UICollectionViewDiffableDataSource — 自動変更アニメーションを備えたモダンな型安全DataSource。
  • カスタムUICollectionViewLayoutは任意のレイアウト(円、螺旋、図)を作成できます。
  • App Storeトップ100の72%のiOSアプリがコンテンツ表示にUICollectionViewを使用しています。
  • シンプルなリストにはUITableViewを、それ以外にはUICollectionViewを使用します。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください