# 画面設計書 77-ColumnarCollectionViewController画面

## 概要

本ドキュメントは、Wikipedia iOSアプリのカラムレイアウトコレクションの基底クラス（ColumnarCollectionViewController）の設計を記述したものである。

### 本画面の処理概要

ColumnarCollectionViewControllerは、UICollectionViewを使用したリスト表示画面の共通基盤を提供する基底クラスである。読みやすさを考慮したカラムレイアウト、ヘッダー/フッターの管理、空状態の表示、リフレッシュコントロール、キーボード対応、画像プリフェッチなど、リスト表示に必要な多くの機能を統合している。

**業務上の目的・背景**：Wikipediaアプリには、ニュース一覧、今日の出来事、検索結果、保存記事一覧など、多くのリスト表示画面が存在する。これらの画面は共通の表示パターンと機能を持つため、基底クラスとして共通ロジックを集約することで、コードの重複を排除し、保守性と一貫性を向上させている。ColumnarCollectionViewLayoutと連携して、デバイスサイズに応じた最適なカラム幅でコンテンツを表示する。

**画面へのアクセス方法**：本クラスは基底クラスであり、直接表示されることはない。NewsViewController、OnThisDayViewController、ArticleLocationCollectionViewControllerなど、多くのViewControllerがこのクラスを継承している。

**主要な操作・処理内容**：
1. UICollectionViewの初期化とレイアウト設定
2. ColumnarCollectionViewLayoutによるカラムレイアウトの適用
3. ヘッダー/フッターのセクション管理
4. 空状態（Empty State）の表示管理
5. リフレッシュコントロール（Pull-to-Refresh）のサポート
6. キーボード表示時のインセット調整
7. 画像のプリフェッチ機能
8. コンテキストメニュー（長押しプレビュー）のサポート
9. iPadでのスクロール時タブバー非表示制御

**画面遷移**：
- 本クラスは基底クラスであり、直接的な画面遷移は定義されていない
- 継承先でArticleCoordinatorなどを使用して記事画面への遷移が実装される

**権限による表示制御**：本クラスでは権限制御は行わない。継承先で必要に応じて実装される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | 記事表示 | 補助機能 | リスト表示画面の共通レイアウト基盤 |

## 画面種別

基底クラス（画面なし）

## URL/ルーティング

本クラスは基底クラスであり、ルーティングは適用されない。

## 入出力項目

| 項目名 | 型 | 入力/出力 | 説明 |
|--------|-----|----------|------|
| headerStyle | HeaderStyle | 入力 | ヘッダースタイル（.sections / .exploreFeedDetail） |
| headerTitle | String? | 入力 | ヘッダーのタイトル |
| headerSubtitle | String? | 入力 | ヘッダーのサブタイトル |
| footerButtonTitle | String? | 入力 | フッターボタンのタイトル |
| emptyViewType | WMFEmptyViewType | 入力 | 空状態の表示タイプ |
| isRefreshControlEnabled | Bool | 入力 | リフレッシュコントロールの有効化 |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| CollectionView | UICollectionView | メインのコレクションビュー |
| ヘッダー | CollectionViewHeader | セクションヘッダー |
| フッター | CollectionViewFooter | セクションフッター（オプション） |
| 空状態ビュー | WMFEmptyView | データがない場合の表示 |
| リフレッシュコントロール | UIRefreshControl | プルトゥリフレッシュ |

## イベント仕様

### 1-viewDidLoad

CollectionViewをビューに追加し、ヘッダー/フッターのセルを登録。キーボード通知の監視を開始。

### 2-viewWillAppear

初回表示時に空状態を更新し、isEmptyDidChange()を呼び出す。選択状態をデセレクト。

### 3-contentSizeCategoryDidChange

コンテンツサイズカテゴリ（Dynamic Type）が変更された時にcollectionView.reloadData()を呼び出す。

### 4-scrollViewDidScroll

スクロール量に応じてiPadでタブバーの表示/非表示を制御。WMFNavigationBarHidingプロトコルに従ってナビゲーションバーの状態を計算。

### 5-リフレッシュ

isRefreshControlEnabledがtrueの場合、プルダウンでrefresh()が呼ばれる。サブクラスでオーバーライドして実装。

### 6-キーボード表示/非表示

keyboardWillChangeFrame/keyboardWillHideでkeyboardFrameを更新し、scrollViewInsetsDidChange()を呼び出す。

### 7-フッターボタン押下

collectionViewFooterButtonWasPressed()が呼ばれる。サブクラスでオーバーライドして実装。

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| - | - | - | 本クラスではデータベース更新は発生しない |

### テーブル別更新項目詳細

本クラスはデータベース操作を行わない。継承先で必要に応じて実装される。

## メッセージ仕様

本クラスでは直接的なメッセージ表示は行わない。空状態表示はWMFEmptyViewに委譲される。

## 例外処理

| 例外ケース | 処理内容 |
|-----------|---------|
| isEmpty == true | wmf_showEmptyView()で空状態ビューを表示 |
| refresh()のデフォルト実装呼び出し | assertでエラー（サブクラスでオーバーライド必須） |

## 備考

- ThemeableViewControllerを継承
- 約570行の大規模なクラス
- 複数のプロトコルを実装: ColumnarCollectionViewLayoutDelegate, UICollectionViewDataSourcePrefetching, CollectionViewFooterDelegate, HintPresenting, WMFNavigationBarHiding
- iOS 18以降のiPadでスクロール時のタブバー制御をサポート
- ArticlePreviewingDelegateの拡張でコンテキストメニューをサポート
- CollectionViewContextMenuShowingの拡張でiOS標準のコンテキストメニューをサポート

---

## コードリーディングガイド

本クラスを理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、レイアウト関連のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ColumnarCollectionViewLayout | `Wikipedia/Code/` | カラムレイアウトの実装 |
| 1-2 | ColumnarCollectionViewLayoutMetrics | `Wikipedia/Code/` | レイアウトメトリクス |
| 1-3 | CollectionViewHeader | `Wikipedia/Code/` | ヘッダービュー |
| 1-4 | CollectionViewFooter | `Wikipedia/Code/` | フッタービュー |

**読解のコツ**: ColumnarCollectionViewLayoutはUICollectionViewLayoutを継承し、読みやすい幅でコンテンツを表示するカラムレイアウトを提供する。

#### Step 2: エントリーポイントを理解する

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ColumnarCollectionViewController.swift | `Wikipedia/Code/ColumnarCollectionViewController.swift` | 本クラスのメインファイル |

**主要処理フロー**:
1. **行19-39**: layoutとlayoutCache、collectionView、layoutManagerのlazy初期化
2. **行49-62**: viewDidLoadでcollectionViewを追加、ヘッダー/フッター登録、キーボード通知監視
3. **行69-90**: viewWillAppearで初回表示処理と選択状態のデセレクト
4. **行192-216**: 空状態の管理（updateEmptyState、isEmptyDidChange）

#### Step 3: レイアウト処理を理解する

カラムレイアウトの動作を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ColumnarCollectionViewController.swift | `Wikipedia/Code/ColumnarCollectionViewController.swift` | ColumnarCollectionViewLayoutDelegate実装 |

**主要処理フロー**:
- **行339-358**: collectionView(_:estimatedHeightForHeaderInSection:forColumnWidth:)
- **行360-372**: collectionView(_:estimatedHeightForFooterInSection:forColumnWidth:)
- **行378-380**: collectionView(_:estimatedHeightForItemAt:forColumnWidth:)
- **行382-384**: metrics(with:readableWidth:layoutMargins:)

#### Step 4: コンテキストメニューを理解する

長押しプレビューの実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ColumnarCollectionViewController.swift | `Wikipedia/Code/ColumnarCollectionViewController.swift` | CollectionViewContextMenuShowingとArticlePreviewingDelegate |

**主要処理フロー**:
- **行499-530**: collectionView(_:contextMenuConfigurationForItemAt:point:)とwillPerformPreviewActionForMenuWith
- **行532-573**: ArticlePreviewingDelegateの実装

### プログラム呼び出し階層図

```
ColumnarCollectionViewController
    │
    ├─ lazy var layout [行19-21]
    ├─ lazy var layoutCache [行23-25]
    ├─ lazy var collectionView [行27-35]
    ├─ lazy var layoutManager [行37-39]
    │
    ├─ viewDidLoad() [行49-62]
    │      ├─ view.wmf_addSubviewWithConstraintsToEdges(collectionView)
    │      ├─ layoutManager.register(CollectionViewHeader)
    │      ├─ layoutManager.register(CollectionViewFooter)
    │      └─ NotificationCenter.addObserver(keyboardWillChangeFrame)
    │
    ├─ viewWillAppear(_:) [行69-90]
    │      ├─ viewWillHaveFirstAppearance() (初回のみ)
    │      │      └─ updateEmptyState()
    │      └─ collectionView.deselectItem()
    │
    ├─ updateEmptyState() [行198-216]
    │      └─ isEmptyDidChange()
    │             └─ wmf_showEmptyView() / wmf_hideEmptyView()
    │
    ├─ apply(theme:) [行273-283]
    │      └─ collectionView.reloadData()
    │
    └─ UICollectionViewDataSource [行461-492]
           ├─ numberOfSections(in:)
           ├─ collectionView(_:numberOfItemsInSection:)
           ├─ collectionView(_:cellForItemAt:)
           └─ collectionView(_:viewForSupplementaryElementOfKind:at:)
```

### データフロー図

```
[入力]                    [処理]                           [出力]

データソース ─────────▶ UICollectionViewDataSource
    │                         │
    │                         ▼
    │                ColumnarCollectionViewLayout
    │                (カラム幅の計算)
    │                         │
    │                         ▼
    │                layoutManager.register()
    │                (セル/ヘッダー/フッター登録)
    │                         │
    └───────────────────────▶ ▼
                       UICollectionView
                       (リスト表示)
                              │
                       ┌──────┴──────┐
                       │             │
                       ▼             ▼
               通常表示        空状態表示
                              (WMFEmptyView)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ColumnarCollectionViewController.swift | `Wikipedia/Code/ColumnarCollectionViewController.swift` | ソース | 本クラス（基底クラス） |
| ThemeableViewController.swift | `Wikipedia/Code/ThemeableViewController.swift` | ソース | 親クラス |
| ColumnarCollectionViewLayout.swift | `Wikipedia/Code/ColumnarCollectionViewLayout.swift` | ソース | カラムレイアウト |
| ColumnarCollectionViewLayoutManager.swift | `Wikipedia/Code/ColumnarCollectionViewLayoutManager.swift` | ソース | レイアウトマネージャー |
| CollectionViewHeader.swift | `Wikipedia/Code/CollectionViewHeader.swift` | ソース | ヘッダービュー |
| CollectionViewFooter.swift | `Wikipedia/Code/CollectionViewFooter.swift` | ソース | フッタービュー |
| WMFEmptyView.swift | `Wikipedia/Code/WMFEmptyView.swift` | ソース | 空状態ビュー |
| HintController.swift | `Wikipedia/Code/HintController.swift` | ソース | ヒント表示コントローラー |
| ArticlePeekPreviewViewController.swift | `Wikipedia/Code/ArticlePeekPreviewViewController.swift` | ソース | 記事プレビュー |
| ArticleCoordinator.swift | `Wikipedia/Code/ArticleCoordinator.swift` | ソース | 記事画面遷移コーディネーター |
