# 画面設計書 65-ScrollableEducationPanel画面

## 概要

本ドキュメントは、Wikipedia iOSアプリのScrollableEducationPanel画面の設計を記載したものです。この画面はアプリ内の様々な機能説明やオンボーディングに使用される汎用的な教育パネルを提供します。

### 本画面の処理概要

ScrollableEducationPanel画面は、ユーザーに新機能の紹介、確認ダイアログ、教育コンテンツを表示するための汎用モーダルパネルです。スクロール可能なコンテンツエリアを持ち、画像、見出し、説明テキスト、プライマリ/セカンダリボタン、フッターを柔軟に構成できます。

**業務上の目的・背景**：アプリ内の様々な機能（Places、通知、編集機能など）には、初回使用時やアップデート時にユーザーに説明が必要な場面があります。この汎用パネルを使用することで、一貫したUIデザインで教育コンテンツを提供し、開発効率を向上させるとともに、ユーザー体験の統一を図っています。

**画面へのアクセス方法**：各機能から初期化パラメータを設定してモーダル表示されます。overFullScreenのモーダルプレゼンテーションスタイルを使用します。

**主要な操作・処理内容**：
1. 閉じるボタン（インライン/ピン止め）によるパネルのdismiss
2. プライマリボタン押下時のアクションハンドラ実行
3. セカンダリボタン押下時のアクションハンドラ実行
4. オプショナルボタン押下時のアクションハンドラ実行
5. 背景タップによるdismiss（設定による）
6. フッター/サブヘッディング内のリンクタップ処理
7. スクロールコンテンツの表示と縦長画面対応

**画面遷移**：
- 遷移元：Places画面、通知関連画面、編集関連画面など多数
- 遷移先：各ボタンアクションに応じた画面（設定画面、認証画面など）

**権限による表示制御**：特になし。呼び出し元で制御されます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 63 | 初回起動ウェルカム | 補助機能 | 各種機能説明に使用 |

## 画面種別

モーダル / 教育パネル

## URL/ルーティング

該当なし（コードからインスタンス化してモーダル表示）

## 入出力項目

| 項目名 | 型 | 入力/出力 | 必須 | 説明 |
|--------|-----|----------|------|------|
| showCloseButton | Bool | 入力 | 必須 | 閉じるボタンの表示有無 |
| primaryButtonTapHandler | ScrollableEducationPanelButtonTapHandler? | 入力 | 任意 | プライマリボタンのタップハンドラ |
| secondaryButtonTapHandler | ScrollableEducationPanelButtonTapHandler? | 入力 | 任意 | セカンダリボタンのタップハンドラ |
| optionalButtonTapHandler | ScrollableEducationPanelButtonTapHandler? | 入力 | 任意 | オプショナルボタンのタップハンドラ |
| dismissHandler | ScrollableEducationPanelDismissHandler? | 入力 | 任意 | dismiss時のハンドラ |
| traceableDismissHandler | ScrollableEducationPanelTraceableDismissHandler? | 入力 | 任意 | dismissトリガーを追跡するハンドラ |
| hasPinnedButtons | Bool | 入力 | 任意 | ボタンをスクロール外に固定するか |
| theme | Theme | 入力 | 必須 | 適用するテーマ |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|----------|------|
| inlineCloseButton / pinnedCloseButton | 右上 | 閉じるボタン |
| imageView | 上部 | 説明用画像 |
| headingLabel | 画像下 | 見出しテキスト |
| subheadingTextView | 見出し下 | 説明テキスト（HTML対応） |
| inlinePrimaryButton / pinnedPrimaryButton | 下部 | プライマリアクションボタン |
| inlineSecondaryButton / pinnedSecondaryButton | プライマリボタン下 | セカンダリアクションボタン |
| inlineOptionalButton | セカンダリボタン下 | オプショナルボタン |
| footerTextView | 最下部 | フッターテキスト（HTML/リンク対応） |
| scrollView | 全体 | スクロールコンテナ |
| roundedCornerContainer | 全体 | 角丸のコンテナビュー |
| gradientView | スクロール端 | スクロールグラデーション |

## イベント仕様

### 1-viewDidLoad

画面がロードされた際の初期化処理。

- reset()でXIBのデフォルト値をクリア
- ボタンのテキストアライメントを中央に設定
- showCloseButton/showOptionalButtonに応じてUI要素を表示/非表示
- buttonStyleに応じたスタイルを適用
- 背景タップジェスチャーを設定
- 閉じるボタンのアクセシビリティラベルを設定

### 2-overlayTapped

背景タップ時のアクション。

- lastActionを.tappedBackgroundに設定
- showCloseButtonまたはdismissWhenTappedOutsideがtrueの場合、画面をdismiss

### 3-close

閉じるボタン押下時のアクション。

- lastActionを.tappedCloseに設定
- 画面をdismiss

### 4-primaryButtonTapped

プライマリボタン押下時のアクション。

- lastActionを.tappedPrimaryに設定
- primaryButtonTappedフラグをtrueに設定
- primaryButtonTapHandlerを呼び出し

### 5-secondaryButtonTapped

セカンダリボタン押下時のアクション。

- lastActionを.tappedSecondaryに設定
- secondaryButtonTapHandlerを呼び出し

### 6-optionalButtonTapped

オプショナルボタン押下時のアクション。

- lastActionを.tappedOptionalに設定
- optionalButtonTapHandlerを呼び出し

### 7-viewWillDisappear

画面が消える際の処理。

- callDismissHandler()を呼び出し
- discardDismissHandlerOnPrimaryButtonTapとprimaryButtonTappedの組み合わせでハンドラ呼び出しを制御

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

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

この画面ではデータベース操作は発生しません。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| - | - | - | 表示コンテンツは呼び出し元から設定される |

## 例外処理

| 例外条件 | 対処 |
|----------|------|
| init(coder:)呼び出し | nil返却 |
| contentStackViewに必須非ゼロ高さ制約 | assertでクラッシュ（開発時に検出） |

## 備考

- UIActivityItemProviderのtypealiasが3種類定義されている
- LastAction enumでdismissトリガーを追跡可能
- ButtonStyleでlegacyStyleとupdatedStyleの2種類のスタイルをサポート
- hasPinnedButtonsがtrueの場合、ボタンをスクロールエリア外に固定配置
- isUrgentがtrueの場合、赤い枠線でコンテナを強調
- isLoadingでプライマリボタンにスピナーを表示
- UITextViewDelegateを実装し、フッター/サブヘッディング内のリンクタップを処理

---

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

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

### 推奨読解順序

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

型定義とプロパティ構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ScrollableEducationPanelViewController.swift | `Wikipedia/Code/ScrollableEducationPanelViewController.swift` | typealias, LastAction enum, ButtonStyle enum |

**読解のコツ**: ScrollableEducationPanelButtonTapHandler, ScrollableEducationPanelDismissHandler, ScrollableEducationPanelTraceableDismissHandlerの3つのtypealiasがコールバックの型を定義しています。

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

初期化とライフサイクルを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ScrollableEducationPanelViewController.swift | `Wikipedia/Code/ScrollableEducationPanelViewController.swift` | init, viewDidLoad, viewWillAppear |

**主要処理フロー**:
1. **336-347行目**: init (dismissHandler版) - 基本的な初期化
2. **349-363行目**: init (traceableDismissHandler版) - 追跡可能なdismiss
3. **369-428行目**: viewDidLoad - UI初期化、ジェスチャー設定、テーマ適用
4. **451-454行目**: viewWillAppear - サブビューの表示/非表示調整

#### Step 3: イベントハンドリングを理解する

ボタンアクションとdismiss処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ScrollableEducationPanelViewController.swift | `Wikipedia/Code/ScrollableEducationPanelViewController.swift` | close, primaryButtonTapped, secondaryButtonTapped, viewWillDisappear |

**主要処理フロー**:
- **513-516行目**: close - 閉じるボタンアクション
- **518-525行目**: primaryButtonTapped - プライマリボタンアクション
- **527-533行目**: secondaryButtonTapped - セカンダリボタンアクション
- **544-548行目**: viewWillDisappear - dismissハンドラ呼び出し

#### Step 4: UITextViewDelegateを理解する

リンクタップの処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ScrollableEducationPanelViewController.swift | `Wikipedia/Code/ScrollableEducationPanelViewController.swift` | textView(_:shouldInteractWith:in:) |

**主要処理フロー**:
- **617-627行目**: textView(_:shouldInteractWith:) - footerLinkAction/subheadingLinkActionの呼び出し

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

```
呼び出し元（例: PlacesViewController）
    |
    +-- ScrollableEducationPanelViewController.init(...)
           |
           +-- viewDidLoad()
           |      +-- reset()
           |      +-- overlayTapped ジェスチャー設定
           |      +-- apply(theme:)
           |
           +-- viewWillAppear()
           |      +-- adjustStackViewSubviewsVisibility()
           |
           +-- ユーザーアクション
           |      +-- close() --> dismiss
           |      +-- primaryButtonTapped() --> primaryButtonTapHandler
           |      +-- secondaryButtonTapped() --> secondaryButtonTapHandler
           |      +-- optionalButtonTapped() --> optionalButtonTapHandler
           |      +-- overlayTapped() --> dismiss (条件付き)
           |
           +-- viewWillDisappear()
                  +-- callDismissHandler()
                         +-- traceableDismissHandler?(lastAction)
                         +-- or dismissHandler?()
```

### データフロー図

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

初期化パラメータ            --> ScrollableEducationPanelVC      --> モーダル表示
- showCloseButton                  |
- image                            +-- UI構築
- heading                          |
- subheadingHTML                   v
- primaryButtonTitle          ユーザー操作
- secondaryButtonTitle             |
- theme                            +-- ボタンタップ
                                   |      |
                                   |      v
                                   |   tapHandler実行
                                   |
                                   +-- 閉じる/背景タップ
                                   |      |
                                   |      v
                                   |   dismissHandler実行
                                   |
                                   v
                              lastAction記録 --> traceableDismissHandler
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ScrollableEducationPanelViewController.swift | `Wikipedia/Code/ScrollableEducationPanelViewController.swift` | ソース | メインViewController |
| ScrollableEducationPanelView.xib | `Wikipedia/Code/ScrollableEducationPanelView.xib` | NIB | 画面レイアウト |
| ScrollViewGradientView.swift | `Wikipedia/Code/ScrollViewGradientView.swift` | ソース | スクロールグラデーション |
| AutoLayoutSafeMultiLineButton.swift | `Wikipedia/Code/AutoLayoutSafeMultiLineButton.swift` | ソース | 複数行対応ボタン |
| HtmlUtils.swift | `WMFComponents/Sources/WMFComponents/` | ソース | HTML文字列のパース |
| Theme.swift | `Wikipedia/Code/Theme.swift` | ソース | テーマ定義 |
