書籍リーダー Bookil では、漫画や技術書のページを「めくる感覚」と「見開きの並び」の両方で読ませたい。iOS 標準の PDFKit は片方ずつなら出せますが、両方を同時に成立させられません。妥協案を順に検討した結果、PDFKit の上に乗りきらず、描画層を自前に切り出して育てる方針に振りました。現在もその開発を進めている途中です。
bookil.labee.devBookil - PDF Viewer for iOS | Bookil - PDF Viewer for iOS | Reading History ManagementA PDF viewer that keeps your reading history even after deleting files. Perfect for managing your PDF library on iPhone and iPad.この記事では PDFKit の displayMode と usePageViewController を組み合わせると何が成立しないか、検討した妥協案、そして自前の描画層に振り直した判断の軸を順に扱います。
書籍リーダーで「ページ送り×見開き」を落としたくなかった
Bookil は iOS / iPadOS で PDF や EPUB を読むためのアプリケーションです。技術書、漫画、雑誌、論文といった「書籍の形をしたコンテンツ」を、紙の本に近い感覚でめくれる状態にするのが目標です。
書籍を読む体験には、書籍タイプごとに必要な要素があります。漫画の見開きカットは、左右ページが同じ視野に並んでこそ成立します。技術書の図版は見開きいっぱいに広がる構成が前提のことがあります。とはいえ、紙の本らしさを支えているのはページをめくる動作です。Bookil ではスクロールで連続的に流れていく読書体験と、1ページ単位でスナップする読書体験を、別物として設計します。
落としたくなかったのは次の3点です。
- 1ページ単位でスナップするページ送りで、紙の本のリズムを残す
- 漫画と技術書のために、左右2ページを同じ視野に並べる見開き表示を成立させる
- 右綴じと左綴じ、縦書きと横書きを書籍メタデータから切り替える
この3点のうち、最初の2点が PDFKit の標準機能では同時に成立しません。
PDFKit の displayMode を整理する
PDFView の displayMode には4つの選択肢があります。
| モード | 挙動 |
|---|---|
singlePage | 1ページずつ表示。ページ移動は手動操作 |
singlePageContinuous | 1ページ幅で縦に連続スクロール |
twoUp | 2ページ並びを表示。スプレッド単位で手動切り替え |
twoUpContinuous | 2ページ並びを連続スクロール |
これに加えて、usePageViewController(_:withViewOptions:) を有効にすると、内部で UIPageViewController が動き、横方向のスワイプでページがスナップします。紙の本をめくる感覚に最も近い挙動です。
組み合わせは一見すると4×2で8通りに見えます。実際にはそうなりません。usePageViewController を有効にしたとき、内部のレイアウトは1ページずつのスナップ送りに固定されます。displayMode に .twoUp を指定しても、見開き並びとページ送りスナップは同時には成立しません。
逆に、.twoUp 単体で見開き表示にすると、見開き単位の手動切り替えは行えますが、ページ送りのスナップ挙動やめくりアニメーションは提供されません。スワイプ操作やジェスチャーはすべて自前で書きます。
妥協案を3つ並べた
描画層を自前で持つコストは小さくありません。判断の前に、PDFKit の範囲でどこまで耐えられるかを順に検討しました。
案A .singlePage + usePageViewController
ページ送りのスナップ感は得られます。一方で見開きは成立しません。漫画の見開きカットは左右に分断され、技術書の見開き図版も同様です。書籍タイプによってはコンテンツの読み方そのものが崩れます。
案B .twoUpContinuous
見開き並びで連続スクロールするモードです。図版は割れず、2ページ分の情報量が常に視野に入ります。ただし、紙の本をめくる感覚は失われます。スクロール量と「現在のページ」の対応がぼやけ、しおりや読書進捗の指標が連続値になります。漫画のように1見開きごとに視線をリセットする読み方では、連続スクロールが読書のリズムを壊します。
案C .twoUp + 自前ジェスチャー
スプレッド単位の手動切り替えに、自前のスワイプジェスチャーを足す案です。見開きとページ送りの両立はできそうに見えます。しかし usePageViewController が暗黙に提供しているページ間アニメーション、エッジでのバウンス、ジェスチャーの中断と復帰、慣性スクロールの調整といった細部を、すべて一から書き直します。
案Cまで踏み込むと、PDFKit の表示パイプラインだけ借りて、操作系を全部書き直す状態に近くなります。PDFKit 本体の抽象に乗っているメリットが薄れていきます。
3案とも、書籍リーダーのコア UX のどちらかを諦めるか、PDFKit の抽象に上塗りで継ぎ足す形になりました。書籍タイプによってはコンテンツが破綻するため、製品として選びにくい結論です。
自前の描画層に振った3つの判断軸
最終的に Bookil では、PDF のデコード自体は標準の下回りに任せ、ページの描画と並び方とめくり方の制御を自前の層で持つ設計に振りました。実装の具体は非公開ですが、判断の軸は次の3点に整理されます。
1つ目は、書籍リーダーの中心 UX である「ページ送り×見開き」は妥協項目にできない、と判断したことです。Bookil で対象にしている書籍タイプには、漫画と技術書が含まれます。どちらも見開きの設計が崩れた瞬間にコンテンツの読み方が変わります。ここを連続スクロールやページ分断で代替する選択肢は、書籍リーダーとしての出発点を否定する形になります。
2つ目は、PDFKit の displayMode が書籍タイプの幅に対して粒度が粗すぎる点です。Bookil では右綴じの漫画、左綴じの技術書、1ページ目を単独で扱う雑誌、奇数ページから始まる章扉といった構成を扱います。書籍メタデータから読み方を組み立てるには、4択フラグでは表現しきれない領域があります。
3つ目は、抱え込む領域を限定できると見立てたことです。PDF の仕様は広大で、フォント埋め込み、注釈、リンク、フォーム、トランスペアレンシ、暗号化された PDF といった機能がぶら下がります。これを全部自前で持つのは現実的ではありません。デコードと描画の下回りは標準フレームワークに任せ、ページの並び方とめくり方の制御だけを自前の層に切り出す方針なら、保守する境界線を引けます。
この3点が揃ったため、書籍リーダーのコア UX に合わせて土台を作り直す判断に踏み切りました。
いまどこまで動いているか
描画層は現在も育てている途中で、右綴じ漫画と縦書き / 横書き混在の書籍タイプで「めくる」「並べる」「進む」のリズムが形になってきています。PDFKit に乗っていた時代に出せていた読書体験は概ね再現できる状態まで来ており、いまは細かい挙動の差分を一つずつ詰める段階です。新しい土台に乗せ替えたぶんだけ、めくり始めの当たり判定や最終ページの折り返しといった細部にバグが出やすく、ここを念入りに確認しています。書籍メタデータからの読み方切り替えも実装が進んでいます。
App StoreBookil App - App StoreDownload Bookil by Labee LLC on the App Store. See screenshots, ratings and reviews, user tips, and more apps like Bookil.標準フレームワークと自前のラインをどこに引くか
標準フレームワークを使うべきか、自前で作るべきか、という問いに一般解はありません。判断の軸は、製品のコア UX が標準の抽象でどこまで成立するか、という1点に集約されます。
PDFKit は、PDF ビューアとして広い領域をカバーします。注釈、検索、リンク追従、フォーム入力、暗号化された PDF の扱いまで含めて、汎用の PDF ビューアを作るなら土台として申し分ありません。それでも、PDFKit が想定している読み方は「ドキュメントを読む」読み方です。書籍を読むための「めくる」「並べる」「進む」のリズムは、PDFKit の抽象の外側にあります。
Bookil のケースでは、汎用ビューアと書籍リーダーのズレが、usePageViewController と .twoUp の組み合わせ問題として表面化しました。標準フレームワークの制約に合わせて体験を曲げるより、体験に合わせて土台を作り直すほうが、長期で見て選びやすい道だ、という判断で今のところ進めています。
