# 画面設計書 80-ArticlePopover画面

## 概要

本ドキュメントは、Wikipedia iOSアプリの記事ポップオーバー画面（ArticlePopoverViewController）の設計を記述したものである。

### 本画面の処理概要

ArticlePopoverViewControllerは、Places画面の地図上でピンをタップした際に表示される記事プレビューポップオーバーである。記事のタイトル、説明、現在地からの距離を表示し、「読む」「保存」「共有」のアクションボタンを提供する。ユーザーはポップオーバーから直接記事を開いたり、保存・共有操作を行える。

**業務上の目的・背景**：Places機能では地図上に多数の記事ピンが表示されるが、ユーザーはピンをタップしただけでその記事の概要を把握したい場合が多い。ArticlePopoverViewControllerは、フル画面の記事ページに遷移することなく、記事の基本情報と主要なアクションを提供する軽量なUIとして機能する。これにより、地図探索のフローを中断せずに記事を確認できる。

**画面へのアクセス方法**：Places画面の地図上に表示されるピン（記事マーカー）をタップすることで、そのピンの位置に紐付いてポップオーバーとして表示される。

**主要な操作・処理内容**：
1. 記事のタイトル（HTML対応）、説明、距離を表示
2. 「Read」ボタンで記事詳細画面へ遷移
3. 「Save」ボタンで記事をリーディングリストに保存/保存解除
4. 「Share」ボタンで記事を共有
5. ポップオーバー本体タップでも記事詳細画面へ遷移
6. ボタンタイトルの表示/非表示を動的に調整（スペース不足時）

**画面遷移**：
- 遷移元：Places画面（地図ピンタップ）
- 遷移先：Article画面（読むボタン/本体タップ）

**権限による表示制御**：特になし。全ユーザーが同一の操作・表示を利用可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | 近くの記事（Places） | 補助機能 | Places画面の記事プレビュー表示 |

## 画面種別

ポップオーバー

## URL/ルーティング

ネイティブ画面のため、URLベースのルーティングは存在しない。PlacesViewControllerから直接生成・表示される。

## 入出力項目

| 項目名 | 型 | 入力/出力 | 説明 |
|--------|-----|----------|------|
| article | WMFArticle | 入力 | 表示対象の記事データ |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| タイトル | WMFArticle.displayTitleHTML | 記事タイトル（HTML対応、Georgia書体） |
| サブタイトル | WMFArticle.capitalizedWikidataDescriptionOrSnippet | 記事の説明またはスニペット |
| 距離 | descriptionLabel | 現在地からの距離（外部から設定） |
| 保存ボタン | SaveButton | 保存/保存済みの切り替え |
| 共有ボタン | UIButton | 共有アクション |
| 読むボタン | UIButton | 記事を開くアクション |

## イベント仕様

### 1-本体タップ（handleTapGesture）

articleSummaryViewにUITapGestureRecognizerが設定されており、タップすると delegate?.articlePopoverViewController(didSelectAction: .read) が呼ばれる。

### 2-保存ボタン押下（save）

@IBAction save(_ sender: Any)で delegate?.articlePopoverViewController(didSelectAction: .save) を呼び、その後 update() でボタン状態を更新する。

### 3-共有ボタン押下（share）

@IBAction share(_ sender: Any)で delegate?.articlePopoverViewController(didSelectAction: .share) を呼ぶ。

### 4-読むボタン押下（read）

@IBAction read(_ sender: Any)で delegate?.articlePopoverViewController(didSelectAction: .read) を呼ぶ。

### 5-ボタンタイトル調整

viewDidLoadで各ボタンの幅を計算し、すべてが収まらない場合は saveButton と shareButton のタイトルを非表示にして、buttonStackView.distributionを.fillProportionallyに変更する。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | SavedPageList | INSERT/DELETE | 記事の保存/保存解除（デリゲート経由） |

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

データベース更新は本クラスでは直接行わず、ArticlePopoverViewControllerDelegateを通じて呼び出し元に処理を委譲する。

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|---------------|
| short-read-title | ボタン | 常時 | "Read"（CommonStrings.shortReadTitle） |
| short-share-title | ボタン | 常時/スペース不足時非表示 | "Share"（CommonStrings.shortShareTitle） |
| short-save-title | ボタン | 未保存時 | "Save"（CommonStrings.shortSaveTitle） |
| short-unsave-title | ボタン | 保存済み時 | "Saved"（CommonStrings.shortUnsaveTitle） |

## 例外処理

| 例外ケース | 処理内容 |
|-----------|---------|
| displayTitleがnil | accessibilityTitlesに追加されない |
| wikidataDescriptionがnil | accessibilityTitlesに追加されない |
| ボタン幅が収まらない | タイトルを非表示にしてアイコンのみ表示 |

## 備考

- UIViewControllerを継承し、Themeableプロトコルを実装
- xibファイル（ArticlePopoverViewController.xib）からUIを読み込み
- ArticlePopoverViewControllerDelegateでアクションを通知
- SaveButtonはカスタムボタンで、保存状態を視覚的に表示
- タイトルはHtmlUtils.StylesでGeorgia書体を適用
- RTL（右から左）言語対応で、readButtonの矢印画像を反転
- アクセシビリティ対応で、カスタムアクション（保存、共有）を設定
- displayTitleHTMLプロパティでHTML形式のタイトルを保持

---

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

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

### 推奨読解順序

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

まず、ポップオーバーで扱うデータとデリゲートを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticle | `WMF Framework/Core Data/` | 記事データモデル |
| 1-2 | WMFArticleAction | `Wikipedia/Code/` | アクションの列挙型 |
| 1-3 | ArticlePopoverViewControllerDelegate | `Wikipedia/Code/ArticlePopoverViewController.swift` | デリゲートプロトコル（行4-6で定義） |

**読解のコツ**: WMFArticleActionは.read, .save, .shareの3つのアクションを定義している。

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

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

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

**主要処理フロー**:
1. **行34-37**: initでWMFArticleを受け取りxibから初期化
2. **行43-73**: viewDidLoadでUI要素の設定とボタンタイトルの調整
3. **行93-129**: update()で保存ボタンの状態とアクセシビリティを更新
4. **行131-135**: configureView()でタイトルのHTML→AttributedString変換

#### Step 3: テーマ適用を理解する

Themeable実装を把握する。

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

**主要処理フロー**:
- **行167-180**: apply(theme:)で各UI要素の色を更新
- ポップオーバー背景、タイトル、サブタイトル、ボタンの色を設定

#### Step 4: アクション処理を理解する

IBActionの実装を把握する。

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

**主要処理フロー**:
- **行142-149**: handleTapGesture()でタップ時に.readアクションを通知
- **行151-154**: save()で.saveアクションを通知後、update()を呼ぶ
- **行156-158**: share()で.shareアクションを通知
- **行160-162**: read()で.readアクションを通知

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

```
ArticlePopoverViewController
    │
    ├─ init(_:) [行34-37]
    │      └─ super.init(nibName: "ArticlePopoverViewController")
    │
    ├─ viewDidLoad() [行43-73]
    │      ├─ UITapGestureRecognizer設定
    │      ├─ ボタンタイトル/画像設定
    │      ├─ ボタン幅チェック・調整
    │      ├─ displayTitleHTML = article.displayTitleHTML
    │      ├─ subtitleLabel.text = article.capitalizedWikidataDescriptionOrSnippet
    │      └─ configureView()
    │
    ├─ update() [行93-129]
    │      ├─ saveButton.saveButtonState = ...
    │      └─ アクセシビリティ設定
    │
    ├─ configureView(withTraitCollection:) [行131-135]
    │      └─ NSAttributedString.attributedStringFromHtml()
    │
    ├─ IBAction save(_:) [行151-154]
    │      ├─ delegate?.articlePopoverViewController(didSelectAction: .save)
    │      └─ update()
    │
    ├─ IBAction share(_:) [行156-158]
    │      └─ delegate?.articlePopoverViewController(didSelectAction: .share)
    │
    ├─ IBAction read(_:) [行160-162]
    │      └─ delegate?.articlePopoverViewController(didSelectAction: .read)
    │
    └─ apply(theme:) [行167-180]
           └─ UI要素の色を更新
```

### データフロー図

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

WMFArticle ──────────▶ viewDidLoad()
    │                        │
    │                        ├──▶ displayTitleHTML
    │                        │         │
    │                        │         ▼
    │                        │    configureView()
    │                        │         │
    │                        │         ▼
    │                        │    titleLabel.attributedText
    │                        │
    │                        └──▶ subtitleLabel.text
    │
    └────────────────────▶ update()
                                │
                                ▼
                         saveButton.saveButtonState
                                │
ユーザー操作 ──────────────────▶ IBAction
    │                              │
    │                    ┌─────────┼─────────┐
    │                    │         │         │
    │                    ▼         ▼         ▼
    │                  save      share     read
    │                    │         │         │
    │                    └─────────┴─────────┘
    │                              │
    │                              ▼
    │               delegate?.articlePopoverViewController
    │               (didSelectAction:)
    │                              │
    └──────────────────────────────┘
                                   │
                                   ▼
                          PlacesViewController
                          (アクション処理)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticlePopoverViewController.swift | `Wikipedia/Code/ArticlePopoverViewController.swift` | ソース | 本クラス |
| ArticlePopoverViewController.xib | `Wikipedia/Code/ArticlePopoverViewController.xib` | XIB | UIレイアウト |
| ArticlePopoverViewControllerDelegate | `Wikipedia/Code/ArticlePopoverViewController.swift` | プロトコル | デリゲートプロトコル（行4-6） |
| WMFArticleAction | `Wikipedia/Code/` | 列挙型 | アクション定義 |
| SaveButton.swift | `Wikipedia/Code/SaveButton.swift` | ソース | カスタム保存ボタン |
| HtmlUtils.swift | `Wikipedia/Code/HtmlUtils.swift` | ソース | HTML→AttributedString変換 |
| WMFArticle | `WMF Framework/Core Data/` | ソース | 記事データモデル |
| Theme.swift | `Wikipedia/Code/Theme.swift` | ソース | テーマ定義 |
| PlacesViewController.swift | `Wikipedia/Code/PlacesViewController.swift` | ソース | 呼び出し元（地図画面） |
