0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZOZOAdvent Calendar 2024

Day 17

PDFKitにおけるページ番号の取得方法

Last updated at Posted at 2024-12-16

はじめに

こんにちは!株式会社ZOZOでiOSエンジニアをしている加藤です。私はショップスタッフの販売サポートツール「FAANS」の開発に従事しています。FAANSでは、この度、ショップスタッフのコーデ投稿を支援するPDFの表示機能が実装され、コーデ投稿に関する情報が掲載されたPDFをアプリ内で閲覧できるようになりました。

FAANSにおけるPDFの閲覧機能はPDFKitを用いて作成されており、PDFの左下に閲覧中のページ数を表示するデザインを採用しています。本記事では、PDFKitを用いて表示したPDFにおいて、閲覧中のページ数を取得し、表示する方法について書きます!

Simulator Screen Recording - iPhone 16 - 2024-12-15 at 17.37.38.gif

目次

  • PDFKitを用いたPDF表示
  • 縦長PDFのページ番号表示
  • 横長PDFのページ番号表示

PDFKitを用いたPDF表示

PDFKitを用いたPDF表示は以下のようなプログラムで行えます。

var document: PDFDocument?

@MainActor
func setupPDFView() {
    let pdfView = PDFView()
    pdfView.displayMode = .singlePageContinuous // PDFを連続で表示する
    pdfView.displayDirection = .vertical // 縦方向に表示する
    pdfView.autoScales = true // 自動的にPDFのスケールを画面に合わせる
    pdfView.document = document
    return pdfView
}

func loadPdfDocument() async {
    do {
        let (data, _) = try await URLSession.shared.data(with: pdfUrl) // URLから非同期でPDFデータを取得
        if let document = PDFDocument(data: data), document.pageCount > 0 {
            self.document = document // 有効なPDFを設定
        } else {
            // 表示できるPDFがないケース
        }
    } catch {
        // 表示できるPDFがないケース
    }
}

extension URLSession {
    func data(with url: URL) async throws -> (Data, URLResponse) {
        try await data(from: url)
    }
}

上記のように、URLを指定してPDFDocumentを生成して、PDFViewのdocumentに設定することでPDFが表示されます。また、.displayMode = .singlePageContinuous.displayDirection = .verticalに設定することでPDFが縦方向に連続で表示されるようにしています。

縦長PDFのページ番号表示

先に提示したGifのように、縦長PDFを表示中に閲覧しているページ番号を取得する方法を紹介します。以下のコードは、ページが変更されるたびに通知を受け取り、現在のページ番号を取得する仕組みです。

let pdfView = PDFView()

NotificationCenter.default.addObserver(forName: .PDFViewPageChanged, object: pdfView, queue: .main) { _ in
    Task { @MainActor in
        // 現在のページ番号を取得
        if let pageNumber = pdfView.currentPage?.pageRef?.pageNumber {
            // pageNumberを画像上に表示
        }
    }
}

このコードでは、NotificationCenterを使用して.PDFViewPageChanged通知を監視しています。この通知は、PDFViewでページをスクロールするなどして表示中のページが変更されるたびにトリガーされます。また、pdfView.currentPageで現在のページを取得でき、pageRef?.pageNumberから現在のページ番号(1始まり)を取得しています。

横長PDFのページ番号表示

横長のPDFを表示する場合、前節と同じようにページ番号を取得すると、初期状態で表示されるページ番号が2になってしまいます。初期のページ番号が2になっているGifを以下に示します。
Simulator Screen Recording - iPhone 16 - 2024-12-15 at 20.53.28.gif

初期のページ番号が2になってしまう原因は、横長PDFの場合、画面上に複数ページが同時に表示されることです。初期の画面では、1〜3ページが表示され、中央に最も近いページが2ページ目のため、取得されるページ数が2となります。FAANSでは、PDFの上端が表示されている場合は、ページ数の表示は1にしたいため、以下のように実装しました。

let pdfView = PDFView()

// ページ変更通知を監視して、現在のページ番号を更新
NotificationCenter.default.addObserver(forName: .PDFViewPageChanged, object: pdfView, queue: .main) { _ in
    Task { @MainActor in
        // 現在のページ番号を取得し、上端が表示されている場合は1を設定
        if let pageNumber = pdfView.currentPage?.pageRef?.pageNumber {
            currentPage = showPdfTop ? 1 : pageNumber
        }
    }
}

// scrollViewのcontentOffsetを監視してPDFのスクロール位置を検出
if let scrollView = pdfView.scrollView() {
    cancellableStore.cancellable = scrollView.publisher(for: \.contentOffset)
        .sink { [weak pdfView] contentOffset in
            Task { @MainActor in
                guard let pdfView = pdfView else { return }
                    if contentOffset.y <= 0.0 {
                        // PDFの最上部が表示されている場合
                        currentPage = 1
                        showPdfTop = true
                    } else if let pageNumber = pdfView.currentPage?.pageRef?.pageNumber, pageNumber != currentPage {
                        // ページが変更された場合に通知を送信
                        NotificationCenter.default.post(name: .PDFViewPageChanged, object: pdfView)
                        showPdfTop = false
                    }
                }
            }
}

fileprivate extension PDFView {
    // PDFViewのサブビューからUIScrollViewを取得
    func scrollView() -> UIScrollView? {
        return self.subviews.compactMap { $0 as? UIScrollView }.first
    }
}

このプログラムでは、PDFViewからスクロール位置を検出するために内部のUIScrollViewを取得し、そのcontentOffsetをCombineのPublisherを使用して監視しています。contentOffsetが0以下の場合、PDFの最上端が表示されていると判断してページ番号を1に設定します。これにより、横長PDFであっても意図したページ番号を正しく表示できます。

まとめ

PDFKitを用いてPDFを表示する方法と、縦長・横長のPDFで現在のページ番号を取得する方法を紹介しました。この記事が、今後PDFKitを用いてiOSアプリにPDF表示機能を実装する方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?