preface

After iOS 11, Apple opened the PDFKit SDK on the iOS platform, which is mainly used to display and operate PDF files. This time, by learning PDFKit, it realized the functions of displaying PDF contents, thumbnails, directories, favorites and searches.

The official documentation

Code iBook, pure Swift written, want to learn PDFKit, you can click Star to learn.

PDFKit

Documentation in PDFKit

// Views PDFView: PDFView is used to display the PDF file. PDFThumbnailView: With PDFThumbnailView, you can get the thumbnail of the PDFCopy the code
// Model PDFDocument: represents PDF data or PDF files and defines methods for writing, searching, and selecting PDF data. PDFPage: represents a page in a PDF. You can obtain information about each page through PDFPage. PDFOutline: Represents the PDF document structure of the tree structure of the hierarchy of elements, you can get PDF outline directory information. PDFSelection: Identifies consecutive or non-consecutive selected text in the Chinese text of a PDF document. It indicates that a text is selected and can be searched in the search module.Copy the code
// Annotations PDFAnnotation: Represents the Annotations in a PDF document. PDFAction: the hop operation performed when a PDF comment is activated or an outline directory is clicked. PDFDestination: Describes the hop directory on the PDF page and is used in the hop page. PDFBorder: Optional comment border drawn entirely within the comment rectangle.Copy the code

iBook

Library page to obtain PDF related data, which can be obtained from KVC.

  • PDF title
if let title = documentAttributes["Title"] as? String {
    cell.title = title
}
Copy the code
  • PDF author
if let author = documentAttributes["Author"] as? String {
	 cell.author = author
}
Copy the code
  • Get the first page as the cover, which has been cached
let thumbnailCache = NSCache<NSURL.UIImage> ()let downloadQueue = DispatchQueue(label: "com.jovins.pdfview.thumbnail")
if let page = document.page(at: 0), let key = document.documentURL as NSURL? {     
    cell.url = key
    if let thumbnail = thumbnailCache.object(forKey: key) {
        cell.image = thumbnail
    } else {
        downloadQueue.async {
            let imgWidth: CGFloat = (UIScreen.main.bounds.width - 48)/2 - 24
            let imgHeight: CGFloat = 190
            let thumbnail = page.thumbnail(of: CGSize(width: imgWidth, height: imgHeight), for: .cropBox)
            self.thumbnailCache.setObject(thumbnail, forKey: key)
            if cell.url = = key {
                DispatchQueue.main.async {
                    cell.image = thumbnail
                }
            }
        }
    }
}
Copy the code

Initialize a DocumentModel to store PDFDocument data.

struct DocumentModel {
    
    var title: String = ""
    var author: String = ""
    var coverImage: UIImage?
    var url: URL?
}
Copy the code
class BookManager {
    
    static let shared = BookManager(a)func getDocument(_ pdfDoc: PDFDocument?). -> DocumentModel {
        
        var model = DocumentModel(a)if let document = pdfDoc, let documentAttributes = document.documentAttributes {
            if let title = documentAttributes["Title"] as? String {
                model.title = title
            } else {
                model.title = "No Title"
            }
            
            if let author = documentAttributes["Author"] as? String {
                model.author = author
            } else {
                model.author = "No Author"
            }
            
            if document.pageCount > 0.let page = document.page(at: 0) {
                let imgWidth: CGFloat = (UIScreen.main.bounds.width - 48)/2 - 24
                let imgHeight: CGFloat = 190
                let thumbnail = page.thumbnail(of: CGSize(width: imgWidth, height: imgHeight), for: .cropBox)
                model.coverImage = thumbnail
            }
            
            if let url = document.documentURL {
                model.url = url
            }
        }
        return model
    }
}
Copy the code

PDF browsing

Figure 1 is the PDF viewing details page

private lazy var pdfView: PDFView = {  
    let view = PDFView()
    view.backgroundColor = Device.bgColor
    view.autoScales = true
    view.displayMode = .singlePage
    view.displayDirection = .horizontal
    view.usePageViewController(true, withViewOptions: [UIPageViewController.OptionsKey.spineLocation: 20])
    return view
}()
self.pdfView.document = self.document
Copy the code

Figure 2 shows all the pages of the PDF

/// The core code of the page
if let doc = self.document, let page = doc.page(at: indexPath.item) {   
    let pageNumber = indexPath.item
    cell.pageNumber = pageNumber
    let key = NSNumber(value: pageNumber)
    if let thumbnail = self.thumbnailCache.object(forKey: key) {
        cell.image = thumbnail
    } else {
        let cellSize = CGSize(width: (UIScreen.main.bounds.width - 16 * 4)/3, height: 140)
        downloadQueue.async {
            let thumbnail = page.thumbnail(of: cellSize, for: .cropBox)
            self.thumbnailCache.setObject(thumbnail, forKey: key)
            if cell.pageNumber = = pageNumber {
                DispatchQueue.main.async {
                    cell.image = thumbnail
                }
            }
        }
    }
}
Copy the code

Figure 3 shows the PDF table of contents

/// This page is the core code
let outline = self.lines[indexPath.item]
cell.titleString = outline.label
cell.pageString = outline.destination?.page?.label

var indentationLevel = -1
var parent = outline.parent
while let _ = parent {
    indentationLevel + = 1
    parent = parent?.parent
}
cell.indentationLevel = indentationLevel
Copy the code

Figure 4 shows the page favorites while browsing.

/// get the favorites page
if let documentURL = self.document?.documentURL?.absoluteString, let bookmarks = UserDefaults.standard.array(forKey: documentURL) as? [Int] {
    self.bookmarks = bookmarks
    self.collection.reloadData()
}
// Display favorites
let pageNumber = self.bookmarks[indexPath.item]
if let page = self.document?.page(at: pageNumber) {
    cell.pageNumber = pageNumber
    let key = NSNumber(value: pageNumber)
    if let thumbnail = self.thumbnailCache.object(forKey: key) {
        cell.image = thumbnail
    } else {
        let cellSize = CGSize(width: (UIScreen.main.bounds.width - 16 * 4)/3, height: 140)
        downloadQueue.async {
            let thumbnail = page.thumbnail(of: cellSize, for: .cropBox)
            self.thumbnailCache.setObject(thumbnail, forKey: key)
            if cell.pageNumber = = pageNumber {
                DispatchQueue.main.async {
                    cell.image = thumbnail
                }
            }
        }
    }
}
Copy the code

Print, direct call system method.

let printInteractionController = UIPrintInteractionController.shared
printInteractionController.printingItem = self.document?.dataRepresentation()
printInteractionController.present(animated: true, completionHandler: nil)
Copy the code

Reading history

Store history in the PDF viewer page

private func storgeHistoryList(a) {
    if let documentURL = self.document?.documentURL?.absoluteString {
        let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].absoluteString
        let key = cache.appending("com.jovins.ibook.storgeHistory")
        if var documentURLs = UserDefaults.standard.array(forKey: key) as? [String] {
            if !documentURLs.contains(documentURL) {
                // If it does not exist, store it
                documentURLs.append(documentURL)
                UserDefaults.standard.set(documentURLs, forKey: key)
            }
        } else {
            // First storage
            UserDefaults.standard.set([documentURL], forKey: key)
        }
    }
}
Copy the code

Gets browsing records

fileprivate func refreshData(a) {
        
    let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].absoluteString
    let key = cache.appending("com.jovins.ibook.storgeHistory")
    if let documentURLs = UserDefaults.standard.array(forKey: key) as? [String] {
        var urls: [URL] = []
        for str in documentURLs {
            if let url = URL(string: str) {
                urls.append(url)
            }
        }
        self.documents = urls.compactMap { PDFDocument(url: $0)}self.tableView.reloadData()
    }
}
Copy the code

search

Set up the proxy with PDFDocument and then call beginFindString to implement the search capability.

func searchBar(_ searchBar: UISearchBar.textDidChange searchText: String) {
    let searchText = searchBar.text!.trimmingCharacters(in: CharacterSet.whitespaces)
    if searchText.count < 3 {
        return
    }
    self.searchResults.removeAll()
    self.tableView.reloadData()
    if let document = self.document {
        document.cancelFindString()
        document.delegate = self
        document.beginFindString(searchText, withOptions: .caseInsensitive)
    }
}
/// proxy implementation method, can obtain the search results
func didMatchString(_ instance: PDFSelection) {
    self.searchResults.append(instance)
    self.tableView.reloadData()
}
Copy the code

In the future

Future will continue to add TXT and EPUB format related functions.