Apple’s PDFKit supports a variety of basic annotation types out of the box, but it doesn’t support other ones, like image annotations, which we talked about in a previous blog post. However, in this blog post, we’ll talk about the annotation types PDFKit does support how they compare to the ones we have at PSPDFKit.
For the upcoming examples, we first need a PDF document. We can either use an already existing one, or we can create a new one with PDFKit. Creating a new PDF with PDFKit is explained in our Creating a PDF in Swift blog post. The sample project for this can be downloaded here.
Let’s start with something simple: a basic free text annotation. It’s mainly used for taking notes on a PDF or adding additional information on a page.
Adding some text to a PDF can’t be that hard, right? Let’s take a look:
let freeTextAnnotation = PDFAnnotation(bounds: CGRect(x: 200, y: 400, width: 200, height: 50), forType: .freeText, withProperties: nil) freeTextAnnotation.fontColor = .red // We need to set this to clear, otherwise the background will be yellow by default. freeTextAnnotation.color = .clear freeTextAnnotation.contents = "Free Text" freeTextAnnotation.font = .systemFont(ofSize: 40) pdfView?.currentPage?.addAnnotation(freeTextAnnotation)
This is straightforward. We create a
PDFAnnotation for the
freeText type, and then we can customize its appearance a bit and set the contents of the annotation, which is the text displayed in the free text annotation. One thing that’s a bit weird is that the background color of the free text annotation is yellow by default, which is fine when wanting to highlight the annotation. However, in most cases, this is probably not the desired effect, so setting it to
.clear will give us a nice and neutral result.
This is how it’s done in PSPDFKit:
let freeTextAnnotation = FreeTextAnnotation(contents: "Free Text") freeTextAnnotation.textBoundingBox = CGRect(x: 200, y: 400, width: 200, height: 50) freeTextAnnotation.color = .red freeTextAnnotation.fontSize = 40 // Set the `pageIndex` for the annotation to specify on which page // it should be added. This defaults to the first page of the document. freeTextAnnotation.pageIndex = 0 document.add(annotations: [freeTextAnnotation], options: nil)
At first glance, there isn’t much difference between the above and what PDFKit does, except we can initialize our free text annotation with our contents, while PDFKit lets us initialize it with the bounding box. The background color for our free text annotation defaults to
.clear, so there aren’t any changes to make there. To control on which page of the document the annotation is added, we can set the
pageIndex property. This defaults to
0, which is the first page of the document. We also can’t edit the free text annotation in PDFKit, while we have a UI for that in PSPDFKit. All in all, the setup is similar, with only minor differences.
Let’s see how they look on a device:
Let’s continue by creating an ink annotation. It’s one of the most basic annotation types and also very common. In fact, it’s probably the single most flexible annotation type, as it can be used to take notes, circle specific parts of a page, and even highlight text.
So how can we create an ink annotation in PDFKit? Let’s see:
// Create a line for the ink annotation. let path = UIBezierPath() path.move(to: CGPoint(x: 300, y: 300)) path.addLine(to: CGPoint(x: 300, y: 400)) path.addLine(to: CGPoint(x: 350, y: 500)) // To change the line width/thickness of an ink annotation, we need to create a border for it // and set the `lineWidth` property there. let border = PDFBorder() border.lineWidth = 5.0 let inkAnnotation = PDFAnnotation(bounds: page.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil) inkAnnotation.border = border inkAnnotation.color = .blue inkAnnotation.add(path) pdfView?.currentPage?.addAnnotation(inkAnnotation)
This process isn’t as straightforward as with the free text annotation. Here we create a
UIBezierPath, which will be the shape of our ink annotation. We decided to opt for a nice and simple line. Changing the thickness of an ink annotation is a bit weird, since we need to create a
PDFBorder first, set the
lineWidth property of the border, and then assign this border to our ink annotation. We could also set the
style property of our border to
.dashed if we want to create a dashed ink annotation. Then we can change the color of the annotation again, and that’s it.
Let’s take a look at how this can be achieved in PSPDFKit:
// Create a line for the ink annotation. let lines = [ [DrawingPoint(cgPoint: CGPoint(x: 300, y: 300)), DrawingPoint(cgPoint: CGPoint(x: 300, y: 400)), DrawingPoint(cgPoint: CGPoint(x: 350, y: 500))] // We can add more lines or other shapes of ink annotations here. ] let inkAnnotation = InkAnnotation(lines: lines) inkAnnotation.lineWidth = 5 inkAnnotation.color = .blue document.add(annotations: [inkAnnotation], options: nil)
It’s already apparent that not as much code is needed in PSPDFKit as in PDFKit. We create an array of arrays of
DrawingPoints to shape our ink annotation. Then we create the
InkAnnotation from those lines, and we set the
color properties to customize the appearance of the annotation. Finally, we add the ink annotation on the document and we’re done!
But wait, if we take a look at the comparison now, we might notice a difference:
As can be seen in the images above, the PDFKit annotation is edgy, while the PSPDFKit annotation is rounded. This is because of a Bézier curve in
DrawingPoint, which makes the latter seem more like a natural, hand-drawn ink annotation.
But what if we really want the annotation to have proper edges? No problem at all; that’s where the
PolyLineAnnotation comes into play. It’s essentially an ink annotation with only straight lines connected to each other, which means it has proper edges. This can be done as follows:
let inkAnnotation = PolyLineAnnotation(points: [CGPoint(x: 300, y: 300), CGPoint(x: 300, y: 400), CGPoint(x: 350, y: 500)]) inkAnnotation.lineWidth = 5 inkAnnotation.color = .blue document.add(annotations: [inkAnnotation], options: nil)
Again, this is an easy way to create an annotation. Here we create a
CGPoints and then customize the appearance before adding it to the document.
And there we have it, the edgy ink annotation:
Last but not least, why not create something a little more advanced, like a text form field? Its main purpose is to let users enter information on a PDF page. Unlike the free text annotation, which we talked about above, a text form field can be edited any time, even in PDFKit. So how do we create one?
let textFormField = PDFAnnotation(bounds: CGRect(x: 200, y: 400, width: 200, height: 50), forType: .widget, withProperties: nil) textFormField.widgetFieldType = .text // Here we can set a placeholder string for the form field. textFormField.widgetStringValue = "Form Field" textFormField.font = .systemFont(ofSize: 40) // Since a border for the form field would only be visible when it's selected, // let's add some background color so it's a bit more noticeable. textFormField.backgroundColor = UIColor.blue.withAlphaComponent(0.25) pdfView?.currentPage?.addAnnotation(textFormField)
This one is surprisingly easy in PDFKit. We just need to create a
PDFAnnotation with a
.widget subtype. Then we need to specify it even further, and we have to set
.text to clarify that we want to create a text form field. Afterward, we can also customize some things, like setting a placeholder, changing the font and font size, and changing the background color for better visibility. How does PSPDFKit do this?
let textFieldFormElement = TextFieldFormElement() textFieldFormElement.boundingBox = CGRect(x: 200, y: 400, width: 200, height: 50) textFieldFormElement.fontSize = 40 // Insert a form field for the form element. It’ll automatically be added to the document. try! TextFormField.insertedTextField(withFullyQualifiedName: "FieldName", documentProvider: document.documentProviders.first!, formElement: textFieldFormElement) // We set a placeholder text for the form field here. // This needs to be done after we've inserted a form field for the form element. textFieldFormElement.contents = "Form Field"
In PSPDFKit, form fields are split into two parts: a field object and an annotation object. While the field object handles the state of the form field and offers methods for modifying it, the annotation object’s main purpose is to provide a graphical element on the PDF.
Here we first create the annotation object, the
TextFieldFormElement. We can set a font size, but we don’t need to add a background color, since it already has one by default. Then we need to insert a form field for the form element with
insertedTextFieldWithFullyQualifiedName:documentProvider:formElement:error:. This will also automatically add the form field to the document. We can then set a placeholder text if we want to.
Let’s see how the created text form fields look:
While PSPDFKit is easily the cleaner and more fleshed out solution, it’s still surprising to see some of the straightforward solutions PDFKit provided for the use cases shown in this blog post. I expected it to be frustrating and time-consuming, but most of the time, I found the correct API fairly quickly.
However, a disadvantage of PDFKit when compared to PSPDFKit is its lack of documentation — both the API docs and the lack of proper guides or examples — especially when doing more complicated stuff. I searched for some official ones myself, but I was disappointed to only find posts from other users searching for the same things I was.
Another major disadvantage of PDFKit — maybe even more so than the lack of documentation — is its lack of UI. Meanwhile, our highly configurable UI is a key feature of the PSPDFKit SDK and the mark of a great user experience. With PSPDFKit, we can add, change, and delete all kinds of annotations on the fly and with ease, which makes working with, and more importantly, annotating PDFs simpler and smoother.