在 SwiftUI 中透過 PDFKit 框架顯示 PDF 文件,需要定義兩個 View 元件,並且匯入 PDFKit 框架,如下:
import PDFKit
第一個 View 元件用來顯示 PDF 內容,並且把 PDFView 實體傳出去,作為後續要顯示縮圖時使用。如果 PDF 有設定密碼(例如1234)程式中自動解鎖,當然把解鎖部分移除就可以讓使用者自行輸入密碼。這個結構應該有更好的寫法,不過目前可 work。
struct DisplayPDF: UIViewRepresentable {
var filename: String
@Binding var pdfView: PDFView?
func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
self.pdfView = pdfView
return pdfView
}
func updateUIView(_ uiView: PDFView, context: Context) {
guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else {
return
}
guard let document = PDFDocument(url: url) else {
return
}
if document.isEncrypted {
document.unlock(withPassword: "1234")
}
uiView.document = document
uiView.autoScales = true
}
typealias UIViewType = PDFView
}
第二個 View 元件用來顯示縮圖,若沒有縮圖需求的話,可以省略。
struct DisplayThumbnailPDF: UIViewRepresentable {
var pdfView: PDFView?
private let size = CGSize(width: 72, height: 88)
func makeUIView(context: Context) -> PDFThumbnailView {
let thumbnail = PDFThumbnailView()
thumbnail.layoutMode = .horizontal
thumbnail.backgroundColor = .darkGray
thumbnail.thumbnailSize = size
return thumbnail
}
func updateUIView(_ uiView: PDFThumbnailView, context: Context) {
uiView.pdfView = pdfView
}
typealias UIViewType = PDFThumbnailView
}
最後主程式,這裡使用 NavigationSplitView 作為排版範例。這裡有兩個小地方比較奇特,比較偏向修正 PDFKit 的內部問題,一個是 onChange 修飾器,目的是讓 PDF 內容切換時,下方的縮圖區大小能夠正常顯示,沒有這個修飾器在模擬器中跑會有問題,但不確定各實機上是否需要這個修飾器。第二個地方在 DisplayThumbnailPDF 後面 frame 修飾器中的寬度值 +2,這是每個縮圖寬度少了 2 點導致縮圖無法正確反應使用者的點選位置。這個值是網路上有人試出來的,請參考這篇文章:https://atomicbird.com/blog/pdfkit-thumbnails/
struct Item: Identifiable {
var filename: String
var id: String { filename }
}
struct ContentView: View {
@State private var selectedId: String?
@State private var pdfView: PDFView?
private let items: [Item] = [
.init(filename: "無密碼.pdf"),
.init(filename: "有密碼.pdf")
]
var body: some View {
NavigationSplitView {
List(items, selection: $selectedId) { item in
Text(item.filename)
}
.onChange(of: selectedId) { newValue in
pdfView = nil
}
} detail: {
if let selectedId {
VStack {
DisplayPDF(filename: selectedId, pdfView: $pdfView)
ScrollView(.horizontal) {
DisplayThumbnailPDF(pdfView: pdfView)
.frame(width: (CGFloat(pdfView?.document?.pageCount ?? 0) + 2) * 72, height: 100)
}
}
}
}
}
}
以上就是在 SwiftUI 中透過 PDFKit 框架顯示 PDF 文件的方式。