5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PDFを表示するビュワーアプリを作った

Last updated at Posted at 2020-11-07

TL;DR

  • PDFKitのサンプルをSwiftUIで作成しました
  • ページをめくるたびに今表示されているPDFのページ番号を取得します
  • で、NotificatonCenterにaddObserveしてPDFViewからページ番号は取得します
  • で、その場合ViewController時代ならViewDidLoadなどで登録したりできますが、どうしたらいいんだろう、、、

と言うあたりを解説します。

デモ

Image from Gyazo

ソースはこちら>https://github.com/dropcontrol/PDFViewer

Viewの解説から

struct ContentView: View {
    @ObservedObject var pdfInfo: PDFInfo = PDFInfo()
    
    var body: some View {
        VStack {
            ShowPDFView(pdfInfo: pdfInfo)
            PdfInfoView(pdfInfo: pdfInfo)
            .padding()
        }.onAppear(){
            pdfInfo.addObserver()
        }
        
    }
}

一番親になるViewでは二つのViewを呼んでいます。一つはPDFのビュワー部分であるShowPDFView()で、もう一つはその下でページ番号とTOPボタンを表示してるPdfInfoView()です。で、そこでpdfInfo: pdfInfoを渡していますが、それはViewの上の

@ObservedObject var pdfInfo: PDFInfo = PDFInfo()

の部分で@ObservedObjectObservableObjectであるPDFInfo()のインスタンスを作成しています。で、そのPDFInfo()は、、、

class PDFInfo: ObservableObject {
    @Published var pageNo: Int = 1
    @Published var pdfView: PDFView = PDFView()
    @Published var stateTopButton: Bool = false
    
    func addObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(self.pageChanged(_:)), name: Notification.Name.PDFViewPageChanged, object: nil)
    }
    
    @objc func pageChanged(_ notification: Notification) {
        pageNo = pdfView.currentPage!.pageRef!.pageNumber
        stateTopButton = pdfView.canGoToFirstPage
        print(self.pageNo)
        print("page is changed")
    }
}

こんな感じになってます。@PublisedではpageNo, pdfView, stateTopButtonといった変数を作っています。その下ではNotificasionCenterへの登録をするfunc addObserver(){}を、notificationを受け取ったら実行される@objc func pageChanged(_ notification: Notification) {}の関数を定義しています。その中ではページ番号と、PDFのトップページかどうかを判断するcanGoToFirstPageです。

で、このなかでPDFの表示のために必要な、

@Published var pdfView: PDFView = PDFView()

を作っています。なので、先の@ObservedObjectで初期化しています。と言うのもpdfInfoを他のViewでも使い回したいので、pdfInfoを読み込んでいるViewに渡しています。

その他のViewについて

PdfInfoViewはUIのところなのでソース読めばわかると思うので割愛して、ShowPDFViewを解説します。ここでは以下のような感じです。

struct ShowPDFView: View {
    @ObservedObject var pdfInfo: PDFInfo
    
    var body: some View {
        PDFViewer(pdfInfo: pdfInfo)
    }
}

ここで用意してる@ObservedObject var pdfInfo: PDFInfoに親からpdfInfoを渡しています。@ObservedObjectでインスタンスを使い回すケースを調べても親からインスタンスを渡す、と言うサンプルはあまり見当たらないのですが、これでやらないと先のObservableObjectで設定した変数を参照できません。

で、ここではPDFViewer(pdfInfo: pdfInfo)で、実際のPDFの表示を行なっています。

PDFViewer()について

PDFの設定についてはUIViewControllerなどでの書き方はほとんど一緒ですが、

struct PDFViewer: UIViewRepresentable {
    @ObservedObject var pdfInfo: PDFInfo
    
    let url: URL = Bundle.main.url(forResource: "Oz was Wizard - Original Soundtrack", withExtension: "pdf")!
    
    func makeUIView(context: UIViewRepresentableContext<PDFViewer>) -> PDFViewer.UIViewType {
    <ここでPDFの設定>
    }
    
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFViewer>) {
    }
    
}

と言う形でUIViewRepresentableを使って実装しています。
参照:

UIKitを使うにあたってはこれと同じような書き方をすれば良いようです。
PDFの設定周りはソースや他のリソースを参考にして貰えば。

このサンプルで出来なかったこと

PDFを拡大/縮小する機能を無効にしたかったんですが、これはPDFKitでは機能的には実装されていないので、まだ調べていますがやり方が分かる方教えてもらえたら嬉しいです。

5
4
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
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?