iOS 11 PDFkit Not Updating Annotation Position

iOS 11 PDFKit not updating annotation position

Well, Since nobody replied. I think there is a bug in the framework, so I'll post what worked for me, after some time of trial and error.

let initialBounds = annotation.bounds
annotation.bounds = CGRect(
origin: locationOnPage,
size: initialBounds.size)
page.removeAnnotation(annotation)
page.addAnnotation(annotation)

It's not elegant, but it does the job

PDFAnnotationSubType Ink not saved when persisting (iOS11, PDFKit)

I hacked my way around the problem. The annotation get saved, but the UIBezierPath does not (and all it's points)

So when I write the PDF back to disk, I go through all the pages, and all annotations, and find those with UIBezierPath, and serialize the CGPoints into JSON into the PDFAnnotation content property.

When reloading the same PDF, I do the exact same thing, which is go through all the pages, and find .ink annotation, deserialize the points, and add the UIBezierPath back on the annotation.

Trigger In-App-Browser after tapping on Annotation with PDFKit - iOS 11

Finally, I figured it out for myself ;) I´ve overlooked one function of the PDFViewDelegate

By setting the delegate of the pdfView to the involved ViewController and using this function

func pdfViewWillClick(onLink sender: PDFView, with url: URL) {
print(url)
}

you are able to use the associated URL of the PDFActionButton and to trigger an In-App-Browser (e.g. SFViewController).

Implement ink annotations on iOS 11 PDFKit document

In the end I solved the problem by creating a PDFViewController class extending UIViewController and UIGestureRecognizerDelegate. I added a PDFView as a subview, and a UIBarButtonItem to the navigationItem, that serves to toggle annotation mode.

I record the touches in a UIBezierPath called signingPath, and have the current annotation in currentAnnotation of type PDFAnnotation using the following code:

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: pdfView)
signingPath = UIBezierPath()
signingPath.move(to: pdfView.convert(position, to: pdfView.page(for: position, nearest: true)!))
annotationAdded = false
UIGraphicsBeginImageContext(CGSize(width: 800, height: 600))
lastPoint = pdfView.convert(position, to: pdfView.page(for: position, nearest: true)!)
}
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: pdfView)
let convertedPoint = pdfView.convert(position, to: pdfView.page(for: position, nearest: true)!)
let page = pdfView.page(for: position, nearest: true)!
signingPath.addLine(to: convertedPoint)
let rect = signingPath.bounds

if( annotationAdded ) {
pdfView.document?.page(at: 0)?.removeAnnotation(currentAnnotation)
currentAnnotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)

var signingPathCentered = UIBezierPath()
signingPathCentered.cgPath = signingPath.cgPath
signingPathCentered.moveCenter(to: rect.center)
currentAnnotation.add(signingPathCentered)
pdfView.document?.page(at: 0)?.addAnnotation(currentAnnotation)

} else {
lastPoint = pdfView.convert(position, to: pdfView.page(for: position, nearest: true)!)
annotationAdded = true
currentAnnotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)
currentAnnotation.add(signingPath)
pdfView.document?.page(at: 0)?.addAnnotation(currentAnnotation)
}
}
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: pdfView)
signingPath.addLine(to: pdfView.convert(position, to: pdfView.page(for: position, nearest: true)!))

pdfView.document?.page(at: 0)?.removeAnnotation(currentAnnotation)

let rect = signingPath.bounds
let annotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)
annotation.color = UIColor(hex: 0x284283)
signingPath.moveCenter(to: rect.center)
annotation.add(signingPath)
pdfView.document?.page(at: 0)?.addAnnotation(annotation)
}
}

The annotation toggle button just runs:

pdfView.isUserInteractionEnabled = !pdfView.isUserInteractionEnabled

This was really the key to it, as this disables scrolling on the PDF and enables me to receive the touch events.

The way the touch events are recorded and converted into PDFAnnotation immediately means that the annotation is visible while writing on the PDF, and that it is finally recorded into the correct position in the PDF - no matter the scroll position.

Making sure it ends up on the right page is just a matter of similarly changing the hardcoded 0 for page number to the pdfView.page(for: position, nearest:true) value.

iOS 11 PDFKit Ink annotation - cannot fill UIBezierPath

I managed to find a solution to the problem that seems relatively optimal.

The trick is do create a subclass of PDFAnnotation and override the draw(with box:, in context:) function. In this function I can use the drawPath(using: .fill) method to fill out bezier path.

The code can look like this:

class SignatureAnnotation : PDFAnnotation {
public var myPath : UIBezierPath = UIBezierPath()

override func draw(with box: PDFDisplayBox, in context: CGContext) {
context.saveGState()
self.page?.transform(context, for: box)
context.beginPath()
context.setLineWidth(0.1)
context.setShouldAntialias(true)
context.addPath(self.myPath.cgPath.mutableCopy()!)
context.drawPath(using: .fill)
context.restoreGState()
}
}

Add this annotation (type .stamp) to the PDF instead of the ink annotation, and everything will be rendered as vector (fully zoomable without being pixelated) - and will be saved together with the rest of the PDF when saved to file or data buffer.

Only drawback is that the UIBezierPath cannot be too complicated as flickering will be introduced if the draw() function takes too long. This can be solved by simply splitting up the UIBezierPath into multiple seperate paths that each has its own annotation.



Related Topics



Leave a reply



Submit