This is the second article about Apple’s PDFkit featuring working with Text Annotations, document auto-saving and PencilKit.
- First article is about PDFKit basics & Ink annotations
- Third article is about creating PDF document on device and inserting/removing pages
PencilKit
We tried to implement PencilKit
support in our app right after iOS 13 release. We even created a ticket to Apple Technical Support asking is there any easy way to do so. Short answer is NO.
You just can't use PencilKit
together with PDFKit
like how it works in native iOS markup screens.
Apple Technical Support answer:
The PencilKit team recommends developers put a PKCanvasView over each PDFPage. Signatures are a little different - you'd probably need to grab the PKDrawing from the PKCanvasView and then render the PKDrawing as an image inside of a PDFPage as you would a watermark.
Proposed solution seems to be impossible to implement (adding PKCanvasView
to PDFPage
). Second part (about grabbing PKDrawing
) seems to be not the right way for our task too because of requirement to zoom page and erase previously added annotations. So, I feel that our solution from the first part of this tutorial is still the right one.
PKToolPicker
seems not to be the right fit as well. It looks much better than out instruments view, but is completely not customizable. For example, in iOS 13 it features ruler, and if we don't need it in our app, we're still unable to remove it from ToolPicker. Hope, Apple would add some ways for customization in future releases.
Text annotations
Text annotations seems to be easier than drawing (Ink). However due to lack of documentation and sample code (again!) it wasn't an easy task.
So, let's assume we need to add some "Hello, world!" text to some place on a document's last page. To do so, write the code below (in your Drawing View Controller, for this example):
func addDateAnnotation() {
guard let document = pdfView.document else { return }
let lastPage = document.page(at: document.pageCount - 1)
let annotation = PDFAnnotation(bounds: CGRect(x: 100, y: 100, width: 100, height: 20), forType: .freeText, withProperties: nil)
annotation.contents = "Hello, world!"
annotation.font = UIFont.systemFont(ofSize: 15.0)
annotation.fontColor = .blue
annotation.color = .clear
lastPage?.addAnnotation(annotation)
}
First, you need a page where you want to add annotation. Then you need to set following properties:
-
contents
: text to display, -
font
: font, -
fontColor
: foreground color, -
color
: background color.
That's it. Just don't forget to save your document.
Annotation types
In one of our tasks we needed to iterate through all annotations array to remove annotation of the specific type. We found that calling PDFAnnotationSubtype.freeText
and annotation.type
would actually return different results!
PDFAnnotationSubtype.freeText // "/freeText"
annotation.type // "FreeText"
So, to filter annotations of the specific type we had to call:
allPageAnnotations.filter { $0.type == "FreeText" }
instead of
allPageAnnotations.filter { $0.type == PDFAnnotationSubtype.freeText }
Next & Previous buttons
Implementing next & previous buttons [which are very useful for your users] is very easy. Just add those buttons in your Storyboard and call one of those methods:
pdfView.goToPreviousPage(nil)
// or
pdfView.goToNextPage(nil)
Auto-saving the document
Due to our long history with crashes we decided to implement auto-saving feature. We wanted to save PDFDocument after each successfully drawn annotation and used this code:
pdfDocument.write(to: url)
However, with larger documents this code caused UI freezes, so we tried to do it in background:
DispatchQueue.global(qos: .background).async {
pdfDocument.write(to: url)
}
Which caused crashes when document's content changed during saving.
Our final solution was to use PDFDocument
method dataRepresentation
combined with simple Timer
:
if let data = pdfDocument.dataRepresentation() {
try? data.write(to: url)
}
This solution didn't cause crashes, but made PDFView
blink on this call. The only working solution we found was:
- create a copy of
PDFDocument
, - apply all changes (adding/removing annotations) to visible document in
PDFView
and to copy, only add completed annotations due to performance, - save copy each 30 seconds (for example),
- track all changes during saving: you cannot apply changes during save, so need to store changes history in memory during saving, which may take some time with a bigger files.
Useful links
You would find some initial information about PDFKit in this WWDC video:
Here you'll find Apple's sample code of advanced drawing with Apple Pencil. Not sure it would work really great with PDFs due to performance issues.
If you're looking for any alternative solution, check one of these frameworks. I haven't found any free or open-source solutions, and licenses for those in the list are pretty expensive ($500-$1K+).
PSPDFKit. Features drop-in replacement APIs for Apple's
- PDFKit,
- Foxit,
- PDFTron.
Top comments (0)