# 画面設計書 42-AppearanceSettings画面

## 概要

本ドキュメントはWikipedia iOSアプリにおける外観設定画面（AppearanceSettingsViewController）の設計内容を記載したものです。

### 本画面の処理概要

AppearanceSettings画面は、Wikipedia iOSアプリの外観に関する各種設定を行うための画面です。ユーザーはテーマ（ライト、セピア、ダーク、ブラック）の選択、画像の暗さ調整、テーブル展開設定、記事テキストサイズの調整、さらに対象ユーザーにはアプリアイコンの変更も可能です。

**業務上の目的・背景**：ユーザーの読書環境を最適化するため、テーマカラーやフォントサイズを個人の好みに合わせて調整できる機能を提供します。特にダークモードや画像の明度調整は、暗い環境での読書や目の疲労軽減に重要な役割を果たします。また、2025年のYear in Review貢献者向けに特別なアプリアイコン選択機能も提供されています。

**画面へのアクセス方法**：設定画面（Settings）から「Reading Preferences」項目をタップすることでこの画面に遷移します。

**主要な操作・処理内容**：
1. 読書テーマの選択（Default、Light、Sepia、Dark、Black）
2. アプリアイコンの選択（対象ユーザーのみ：Default、Contributor）
3. 画像の暗さ調整（Dim images）の切り替え
4. テーブル自動展開設定の切り替え
5. 記事テキストサイズのスライダー調整

**画面遷移**：
- 遷移元：Settings画面
- 遷移先：なし（設定変更のみ）

**権限による表示制御**：アプリアイコン設定セクションは、Year in Review 2025での貢献実績があり、期限内のユーザーにのみ表示されます（`qualifiesForIcon2025`フラグで判定）。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 47 | 外観設定 | 主機能 | テーマ・フォントサイズ等の外観設定 |
| 62 | テーマ適用 | 補助機能 | テーマ切り替えの適用 |

## 画面種別

設定（複合設定画面：選択、スイッチ、スライダー）

## URL/ルーティング

iOSネイティブアプリのため、URLは使用しない。画面遷移はUINavigationControllerのpush遷移で管理。

## 入出力項目

| 項目名 | 項目ID | データ型 | 入力/出力 | 必須 | 説明 |
|--------|--------|----------|-----------|------|------|
| テーマ名 | themeName | String | 入出力 | - | 選択されたテーマの識別子 |
| 画像暗化 | isImageDimmingEnabled | Bool | 入出力 | - | 画像を暗くするかどうか |
| テーブル自動展開 | isAutomaticTableOpeningEnabled | Bool | 入出力 | - | テーブルを自動展開するか |
| テキストサイズ | textSizeMultiplier | Float | 入出力 | - | テキストサイズの倍率 |
| アプリアイコン | isNewIconOn | Bool | 入出力 | - | コントリビューターアイコンを使用するか |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| ナビゲーションタイトル | String | "Reading Preferences" |
| Reading themes セクション | Section | テーマ選択項目群 |
| App Icon セクション | Section | アプリアイコン選択（条件付き表示） |
| Theme options セクション | Section | 画像暗化設定 |
| Table Settings セクション | Section | テーブル展開設定 |
| Adjust article text sizing セクション | Section | テキストサイズ調整 |

## イベント仕様

### 1-テーマ選択

ユーザーがテーマ項目をタップした際の処理。

1. `tableView(_:didSelectRowAt:)`で選択を検知
2. `AppearanceSettingsCheckmarkItem`の`checkmarkAction`クロージャを実行
3. `userDidSelect(theme:)`メソッドでNotificationCenterに`WMFUserDidSelectThemeNotification`を送信
4. アプリ全体にテーマ変更が適用される

### 2-画像暗化スイッチ切替

ユーザーが「Dim images」のスイッチを切り替えた際の処理。

1. `handleImageDimmingSwitchValueChange(_:)`が呼び出される
2. アニメーション遅延後に`applyImageDimmingChange(isOn:)`を実行
3. `userDidSelect(theme:isImageDimmingEnabled:)`でテーマ通知を送信
4. TableViewを再読み込み

### 3-テーブル自動展開スイッチ切替

ユーザーが「Expand tables」のスイッチを切り替えた際の処理。

1. `handleAutomaticTableOpenSwitchValueChange(_:)`が呼び出される
2. アニメーション遅延後に`applyAutomaticTableOpenChange(isOn:)`を実行
3. `UserDefaults.standard.wmf_isAutomaticTableOpeningEnabled`を更新

### 4-アプリアイコン選択

ユーザーがアプリアイコン項目をタップした際の処理。

1. `tableView(_:didSelectRowAt:)`で選択を検知
2. `AppearanceSettingsIconItem`の`checkmarkAction`クロージャを実行
3. `DonateFunnel.shared.logYearInReviewSettingsAppIconDidToggle(isOn:)`でログ記録
4. `AppIconUtility.shared.updateAppIcon(isNew:)`でアイコン変更

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| テーマ選択 | UserDefaults | UPDATE | themeNameキーの値を更新 |
| 画像暗化切替 | UserDefaults | UPDATE | isImageDimmingEnabledキーの値を更新 |
| テーブル展開切替 | UserDefaults | UPDATE | isAutomaticTableOpeningEnabledキーの値を更新 |
| テキストサイズ変更 | UserDefaults | UPDATE | textSizeMultiplierキーの値を更新 |
| アプリアイコン変更 | UserDefaults/iOS設定 | UPDATE | アイコン設定を更新 |

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

#### UserDefaults

| 操作 | 項目（キー名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | themeName | String（default/light/sepia/dark/black） | 選択されたテーマ名 |
| UPDATE | isImageDimmingEnabled | Bool（true/false） | 画像暗化設定 |
| UPDATE | isAutomaticTableOpeningEnabled | Bool（true/false） | テーブル展開設定 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| appearance-settings-reading-themes | ヘッダー | "Reading themes" | テーマセクションヘッダー |
| theme-default-explanation | サブタイトル | "Matches system theme" | デフォルトテーマの説明 |
| appearance-settings-theme-options | ヘッダー | "Theme options" | テーマオプションセクション |
| appearance-settings-image-dimming-footer | フッター | "Decrease the opacity of images on the dark and black themes" | 画像暗化の説明 |
| appearance-settings-set-automatic-table-opening | ヘッダー | "Table Settings" | テーブル設定セクション |
| appearance-settings-expand-tables-footer | フッター | "Set all tables in all articles to be open by default..." | テーブル展開の説明 |
| appearance-settings-adjust-text-sizing | ヘッダー | "Adjust article text sizing" | テキストサイズセクション |
| appearance-settings-set-icon-header | ヘッダー | "App Icon" | アイコンセクション |
| appearance-settings-set-icon-footer | フッター | "The contributor icon celebrates your 2025 in-app contributions..." | アイコンの説明 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| アイコン期限切れ | AppIconUtility.shared.checkAndRevertIfExpired()でデフォルトアイコンに戻す |
| テーマ適用失敗 | viewIfLoadedがnilの場合は処理をスキップ |

## 備考

- 本画面はSubSettingsViewControllerを継承しており、設定画面共通のTableView構造を使用
- カスタムセル（ImageDimmingExampleViewController、TextSizeChangeExampleViewController、FontSizeSliderViewController）を子ViewControllerとして埋め込み
- テーマ変更はNotificationCenterを通じてアプリ全体に通知される
- deinitでcancelPreviousPerformRequestsを呼び出し、保留中の処理をキャンセル

---

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

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

### 推奨読解順序

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

まず、設定項目を表現するデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AppearanceSettingsViewController.swift | `Wikipedia/Code/AppearanceSettingsViewController.swift` | AppearanceSettingsItemプロトコルと各実装構造体（4-49行目） |

**読解のコツ**: AppearanceSettingsItemプロトコルが各設定項目の共通インターフェースを定義し、具体的な実装（DimSwitchItem、CheckmarkItem、IconItem等）がそれぞれの設定タイプを表現しています。

#### Step 2: セクション構築を理解する

設定画面のセクション構成を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AppearanceSettingsViewController.swift | `Wikipedia/Code/AppearanceSettingsViewController.swift` | sectionsForAppearanceSettings()メソッド（89-146行目） |

**主要処理フロー**:
1. **91-95行目**: テーマ選択項目の生成（checkmarkItemヘルパー関数）
2. **99-103行目**: デフォルトテーマを含む全テーマ項目の配列生成
3. **105-106行目**: Reading themesセクションの生成
4. **108行目**: Theme optionsセクション（画像暗化）
5. **110行目**: Table Settingsセクション
6. **112行目**: Text sizingセクション
7. **116-142行目**: 条件付きApp Iconセクションの生成

#### Step 3: セル表示処理を理解する

TableViewのセル生成とカスタムViewControllerの埋め込みを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AppearanceSettingsViewController.swift | `Wikipedia/Code/AppearanceSettingsViewController.swift` | tableView(_:cellForRowAt:)（156-245行目） |

**主要処理フロー**:
- **159-184行目**: カスタムViewControllerをセルに埋め込む処理
- **186-193行目**: スペーサービュー項目の処理
- **212-228行目**: スイッチ項目（DimSwitchItem、AutomaticTableOpenSwitchItem）の設定
- **229-235行目**: アイコン項目の設定

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

ユーザー操作に対する処理フローを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AppearanceSettingsViewController.swift | `Wikipedia/Code/AppearanceSettingsViewController.swift` | テーマ選択とスイッチ処理（262-338行目） |

**主要処理フロー**:
- **262-268行目**: userDidSelect(theme:)でテーマ変更通知を送信
- **279-287行目**: didSelectRowAtでチェックマーク/アイコン項目の選択処理
- **318-328行目**: 画像暗化スイッチのハンドリング
- **330-338行目**: テーブル展開スイッチのハンドリング

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

```
AppearanceSettingsViewController
    │
    ├─ viewDidLoad()
    │      ├─ tableView.register() - セル登録
    │      └─ sectionsForAppearanceSettings() - セクション構築
    │
    ├─ tableView(_:cellForRowAt:)
    │      ├─ AppearanceSettingsCustomViewItem → 子VC埋め込み
    │      │      ├─ ImageDimmingExampleViewController
    │      │      ├─ TextSizeChangeExampleViewController
    │      │      └─ FontSizeSliderViewController
    │      └─ WMFSettingsTableViewCell - 通常セル
    │
    ├─ tableView(_:didSelectRowAt:)
    │      ├─ AppearanceSettingsCheckmarkItem.checkmarkAction()
    │      │      └─ userDidSelect(theme:)
    │      │             └─ NotificationCenter.post()
    │      └─ AppearanceSettingsIconItem.checkmarkAction()
    │             └─ AppIconUtility.shared.updateAppIcon()
    │
    ├─ handleImageDimmingSwitchValueChange()
    │      └─ applyImageDimmingChange()
    │             └─ userDidSelect(theme:isImageDimmingEnabled:)
    │
    └─ handleAutomaticTableOpenSwitchValueChange()
           └─ applyAutomaticTableOpenChange()
                  └─ UserDefaults更新
```

### データフロー図

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

テーマ選択 ───────────▶ checkmarkAction() ──────────────▶ NotificationCenter
                              │                           (アプリ全体に通知)
                              ▼
                       userDidSelect(theme:)
                              │
                              ▼
                       UserDefaults更新

スイッチ操作 ─────────▶ handle*SwitchValueChange() ────▶ UserDefaults更新
                              │
                              ▼
                       apply*Change() [遅延実行]

アイコン選択 ─────────▶ checkmarkAction() ──────────────▶ iOS設定更新
                              │
                              ▼
                       AppIconUtility.updateAppIcon()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AppearanceSettingsViewController.swift | `Wikipedia/Code/AppearanceSettingsViewController.swift` | ソース | メインのViewController |
| SubSettingsViewController.swift | `Wikipedia/Code/SubSettingsViewController.swift` | ソース | 親クラス（設定画面共通基盤） |
| ImageDimmingExampleViewController.swift | `Wikipedia/Code/ImageDimmingExampleViewController.swift` | ソース | 画像暗化プレビュー |
| TextSizeChangeExampleViewController.swift | `Wikipedia/Code/TextSizeChangeExampleViewController.swift` | ソース | テキストサイズプレビュー |
| FontSizeSliderViewController.swift | `Wikipedia/Code/FontSizeSliderViewController.swift` | ソース | フォントサイズスライダー |
| AppIconUtility.swift | `WMFComponents/Sources/WMFComponents/AppIconUtility.swift` | ソース | アプリアイコン管理 |
| Theme.swift | `Wikipedia/Code/Theme.swift` | ソース | テーマ定義 |
| ReadingThemesControlsViewController.swift | `Wikipedia/Code/ReadingThemesControlsViewController.swift` | ソース | テーマ通知定数 |
