シリーズ:iOS版「見開きPDFビューア」開発記録
この記事は、ChatGPTと壁打ちしながら作成しました。また、Gitの変更履歴とソースコードを調査した結果を含みます。
私は本をたくさん買うので、電子書籍を端末に保存して読む方法に限界を感じていました。ストレージ容量の大きい端末に買い換えても、またすぐに容量が足りなくなってしまうからです。
そこで、このPDFビューアでは、クラウドに置いたPDFを快適に開いて読めることも重視しました。Apple製品では、自分のアカウントのiCloud Driveを使う想定です。端末の容量を気にして買い換え続ける状況を、何とかしたいと考えていました。
さて、PDFの読み込み、見開き表示、検索、ズーム。基本的な機能はそろってきましたが、実際に使っていると、まだ気になるところが出てきます。開発と並行して、実機のiPadでも毎日のように試用を続けていました。
前回の記事では、GemmaとClineでiOS用のPDFビューア「MihirakiPDFViewer」を作り、2026年8月25日にChatGPTへ全面移行するまでを振り返りました。
今回は、XcodeでCodexを利用して進めた開発のうち、画質や表示、操作に関わる修正を紹介します。特に画質については、別の機能を追加したところ、通常の表示まで思いがけず改善することになりました。
他のアプリと比べて、画質がよくない
初期のバージョンをiPadで使っていて気づいたのが、PDF表示の画質です。他のアプリで表示したときと比べて、解像度が低いように見えました。
ただ、気になってはいても、具体的にどこをどう直せばよいのか分かりません。そのため、表示処理そのものの改善は後回しにして、高画質化とシャープネスの機能を実装することにしました。8月29日のGit履歴に、この追加が残っています。
すると、高画質化を有効にしたときだけでなく、通常モードの表示もきれいになりました。もともと気になっていた画質が、別の機能を追加したことで改善したわけです。これはラッキーだったと感じています。
通常モードの描画処理も変わっていた
なぜ通常モードまで改善したのか。当時、変更されたコードも確認していました。8月29日の差分を見ると、PDFから画像を作る処理が変わっています。
変更前は、次のように取得した画像を表示していました。
page.thumbnail(of: size, for: .mediaBox)
変更後は、UIGraphicsImageRendererで画像を作り、その描画先へpage.draw(with:to:)でPDFのページを描く処理になっています。この処理は、高画質モードだけでなく通常モードでも使われます。
新しい処理には、画面の倍率を考慮する計算も入っていました。
let screenScale = max(displayScale, 1)
let qualityMultiplier: CGFloat = isHighQualityRenderingEnabled ? 2 : 1
let targetScale = screenScale * qualityMultiplier
高画質化がオフでも画面倍率を使い、オンならさらに2倍を指定する構成です。ただし、描画画像の長辺には通常2048、高画質4096ピクセルの上限があるので、常に指定どおりの倍率になるわけではありません。
初期のコードでは低めの解像度で取得した画像を表示していて、新機能の追加に伴い、以前より高い解像度で描画されるようになったのではないかと思っています。
差分から確認できるのは、サムネイルを取得する処理から、画面倍率を考慮して描画する処理へ変わったことまでです。当時の画像のピクセル数や、同じ条件での比較画像は残していないため、これが画質改善の原因だったとまでは断定できません。
ともあれ、iPadで使っていて気になっていた表示は改善しました。機能を追加したつもりが、普段の表示にもよい影響があったという、少し意外な経緯でした。
検索できても、ハイライトがずれる
画質とは別に、検索結果の表示にも修正が必要でした。文字列が見つかっていても、ハイライトがその文字の上に重なるとは限りません。
前回の記事でも、検索と座標変換の調整には触れました。その後、9月29日にもiPadでのハイライト位置を直す変更が残っています。
変更前のコードでは、ハイライトの水平方向の位置を、ページが中央にあるものとして計算していました。一方、PDFの画像は、見開きの左右に応じて配置しています。画像とハイライトで、位置を決める基準が違っていたわけです。
修正では、ページの左端を求める計算をalignedPageOriginXへまとめ、画像とハイライトの両方で使うようにしています。中央配置の場合は、表示領域の幅から描画幅を引き、その半分を左側の余白にします。
(containerWidth - renderedWidth) / 2
画像は、この左端に描画幅の半分を足した位置を中心にして置きます。ハイライトにも同じ左端の位置を渡すことで、両者の基準をそろえています。
図1:PDF画像とハイライトの位置の基準をそろえる修正。左は画像を右寄せし、ハイライトを中央配置の基準で計算した例、右は両方に同じ原点を使った例です。位置計算の違いを説明する模式図です。
検索の処理が正しくても、その結果を画面のどこへ描くかは別に確認が必要でした。見開きの配置を調整するときには、PDF画像だけでなく、その上に重ねる表示にも目を向ける必要があります。
メニューを出したら、本文の大きさまで変わる
操作部を表示したときのレイアウトも、調整が続いた部分です。10月2日には、操作部を表示してもPDFのレイアウトを安定させる変更があります。
それまでの構成では、PDFとスライダーなどを縦に並べていました。この並べ方だと、操作部を出すことでPDFが使える高さも変わります。表示領域に合わせてPDFを拡大・縮小していれば、メニューを出す操作が、本文の大きさを変えることにもつながります。
このときの修正では、ZStackを使ってPDFの上に操作部を重ねる構成へ変更しています。本文の領域と、操作部の表示を切り離す方向の修正でした。
図2:操作部の配置によるPDF表示の違い。左は操作部の表示でPDFの領域が狭まり、本文も縮小される構成。右は操作部を重ね、本文の大きさを保つ構成です。配置の仕組みを説明する模式図です。
ただ、この構成で終わったわけでもありません。10月14日には標準ツールバーを採用する変更があり、操作部の作り方はその後も見直しています。履歴を振り返ると、その時点で気になった問題に対応しながら、画面の構成も変わっていったことが分かります。
右綴じは、左右を入れ替えるだけではなかった
このアプリで大事にしている右綴じの表示にも、修正が続いていました。10月9日には、右綴じのPDFが最後のページから開く問題への対応があります。
この変更では、TabView全体へ読書方向を設定する処理を外し、右綴じの場合はページグループを逆順に並べる実装へ変えています。
見開きの中で、どちらのページを左に置くか。それと、見開き同士をどの順に並べるか。この二つは分けて考える必要がありました。見た目の左右が合っていても、開いた直後の位置やページ送りまで合っているとは限りません。
図3:見開き内の左右配置と、見開き同士の読書順の違い。下段は、表紙から2・3ページ、4・5ページへ進む例です。図のページ番号は1始まりで、以下のコード説明に使う0始まりの番号とは区別しています。配置と読書順を説明する模式図です。
10月14日には、ページの組み合わせを決める処理がPDFPageGroupingへ切り出されています。ここでは、見開きをページ番号の配列で表します。
以下は、5ページのPDFを0始まりの番号で表した例です。
| 条件 | ページの組み合わせ |
|---|---|
| 左綴じ・表紙なし | [[0, 1], [2, 3], [4]] |
| 右綴じ・表紙なし | [[1, 0], [3, 2], [4]] |
| 左綴じ・表紙あり | [[0], [1, 2], [3, 4]] |
右綴じでは、各見開きの中の左右を入れ替えます。表紙ありなら、先頭のページを単独にします。この配列とは別に、画面上で見開き同士を並べる順序も扱います。
こうして処理が分かれると、画面を開いて確認するだけでなく、ページの組み合わせをテストで確かめられるようになります。
使い続けながら、表示の調整も続ける
前回の記事では、見開きの隙間や配置の修正を何度も繰り返したことを書きました。ChatGPTへ移行した後も、表示の調整は続いています。
実機のiPadで使っていると、画質やハイライトの位置、操作部を出したときの見え方などが気になります。ただ、その場で気づいても、原因や直し方まで分かるとは限りませんでした。画質のように、別の機能を追加した結果、思いがけず改善したこともあります。
画質が改善したときには、変更されたコードも確認していました。通常モードの描画処理も変わっていたので、それが改善につながったのではないかと考えています。
その後のコードでは、位置の計算やページの組み合わせも、個別に確かめられる形へ整理されてきました。次の記事では、再起動すると設定が消える問題などを題材に、テストで何が分かり、どこまで確認できたのかを紹介したいと思います。
ソースコードと本稿の対象
ソースコードはGitHubのtyamada/MihirakiPDFViewerで公開しています。
2026年10月20日時点では、iOS版はApp Storeの審査中で、まだ公開できていません。一方、Android版はGoogle PlayストアでVer.2.0.0を公開中です。Android版の開発についても、この後、別の記事でまとめる予定です。
本稿は2026年10月20日時点の調査に基づき、開発コードはtestingブランチの91f0853を基準にしています。Gitの変更日は開発の記録です。
前の記事:第1回:GemmaとClineでiOS用PDFビューアを作ってみた――見開き表示の修正を繰り返した約2か月
次の記事:第3回:テストPDFと識別・検索・負荷の検証(公開予定)


