Create an eBook reader with PDFKit on Swift

Original author: David Wu
  • Transfer
  • Tutorial
Good day! I present to your attention the continuation of the article “Download, save and view PDF in Swift”, as the author promised - we will take a closer look at PDFKit.

PDFKit appeared in iOS 11, and it has 3 great features about which I would like to tell you in the process of creating Reader eBook application: Paging , displaying the contents and page icon .

Everyone who is interested, welcome under cat.

image

image

image
Paging, Outline, and Thumbnails

Creating PDFViewController with Initializer Injection

When creating the URL variable in PDFViewController, we made it immutable. That is why I chose Initializer Injection , rather than Property Injection or Method Injection .

The only variable to be injected is pdfUrl , while the rest, document and outline , are accessible through a local class area and can be initiated via init () .

The contents of pdfView does not change with us, so I marked it with the document variable and also initialized withinit () .

import UIKit
import PDFKit
class PDFViewController: UIViewController {
    private let pdfUrl: URL
    private let document: PDFDocument!
    private let outline: PDFOutline?
    private var pdfView = PDFView()
    init(pdfUrl: URL) {
        self.pdfUrl = pdfUrl
        self.document = PDFDocument(url: pdfUrl)
        self.outline = document.outlineRoot
        pdfView.document = document
        super.init(nibName: nil, bundle: nil)
    }
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(pdfView)
    }
}

import UIKitclass ViewController: UIViewController {
    @IBAction func openPdfPressed(_ sender: Any) {
        guard let path = Bundle.main.url(forResource: "swift", withExtension: "pdf") else {
            print("failed to unwrap fileURL")
            return
        }
        let pdfViewController = PDFViewController(pdfUrl: path)
        present(pdfViewController, animated: true, completion: nil)
    }
}

Installing horizontal pagination

Now we have the content in the document that we wrote in pdfView.document . In order to make a horizontal scroll, as in the book, you need to add a few settings to PDFView.

private func setupPDFView() {
    view.addSubview(pdfView)
    pdfView.displayDirection = .horizontal
    pdfView.usePageViewController(true)
    pdfView.pageBreakMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    pdfView.autoScales = true
}

image

Page icons for easy navigation

PDFKit has an amazingly handy thing, such as ThumbnailView. All you need to do is assign our pdfView property to the thumbnailView.pdfView . And that's it! No delegates, notifications and other settings.

private func setupThumbnailView() {
    thumbnailView.pdfView = pdfView
    thumbnailView.backgroundColor = UIColor(displayP3Red: 179/255, green: 179/255, blue: 179/255, alpha: 0.5)
    thumbnailView.layoutMode = .horizontal
    thumbnailView.thumbnailSize = CGSize(width: 80, height: 100)
    thumbnailView.contentInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
    view.addSubview(thumbnailView)
}

image

Showing content for navigation by chapters

to get to a specific page, we need to pass in pdfView specific page ( PDFPage ). So we need to delve into the hierarchy of content ( outline ) in a document ( document ) to get to the desired page ( page ).

image

I use UITableViewController as a pop-up window ( popOver ). Therefore, I need to:

  1. Create a protocol. UITableViewController must follow this protocol.
  2. Add some delegate methods to access pdfView in PDFViewController .
  3. Call the delegate method when the user selects a particular cell.

import UIKit
import PDFKit
protocol OutlineDelegate: class {
    func goTo(page: PDFPage)
}
class OutlineTableViewController: UITableViewController {
    let outline: PDFOutline
    weak var delegate: OutlineDelegate?
    init(outline: PDFOutline, delegate: OutlineDelegate?) {
        self.outline = outline
        self.delegate = delegate
        super.init(nibName: nil, bundle: nil)
    }
    ...
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return outline.numberOfChildren
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell =
            tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCellif let label = cell.textLabel, let title = outline.child(at: indexPath.row)?.label {
            label.text = String(title)
        }
        return cell
    }
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let page = outline.child(at: indexPath.row)?.destination?.page {
            delegate?.goTo(page: page)
        }
    }
}

image

Hooray! So we used the 3 main functions of PDFKit. There are still some trivial details of the interface, but in this article we will not focus on them. The project is available under the link for everyone.

Also popular now: