Implement ink annotations on iOS 11 PDFKit document

匿名 (未验证) 提交于 2019-12-03 02:50:02

问题:

I want to allow the user to draw on an iOS 11 PDFKit document viewed in a PDFView. The drawing should ultimately be embedded inside the PDF.

The latter I have solved by adding a PDFAnnotation of type "ink" to the PDFPage with a UIBezierPath corresponding to the user's drawing.

However, how do I actually record the touches the user makes on top of the PDFView to create such an UIBezierPath?

I have tried overriding touchesBegan on the PDFView and on the PDFPage, but it is never called. I have tried adding a UIGestureRecognizer, but didn't accomplish anything.

I'm assuming that I need to afterwards use the PDFView instance method convert(_ point: CGPoint, to page: PDFPage) to convert the coordinates obtained to PDF coordinates suitable for the annotation.

回答1:

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.



回答2:

I've done this by creating a new view class (eg Annotate View) and putting on top of the PDFView when the user is annotating.

This view uses it's default touchesBegan/touchesMoved/touchesEnded methods to create a bezier path following the gesture. Once the touch has ended, my view then saves it as an annotation on the pdf.

Note: you would need a way for the user to decide if they were in an annotating state.

For my main class

class MyViewController : UIViewController, PDFViewDelegate, VCDelegate {  var pdfView: PDFView? var touchView: AnnotateView?  override func loadView() {    touchView = AnnotateView(frame: CGRect(x: 0, y: 0, width: 375, height: 600))    touchView?.backgroundColor = .clear    touchView?.delegate = self    view.addSubview(touchView!) }   func addAnnotation(_ annotation: PDFAnnotation) {     print("Anotation added")     pdfView?.document?.page(at: 0)?.addAnnotation(annotation) } }

My annotation view

class AnnotateView: UIView { var path: UIBezierPath? var delegate: VCDelegate?  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {     // Initialize a new path for the user gesture     path = UIBezierPath()     path?.lineWidth = 4.0      var touch: UITouch = touches.first!     path?.move(to: touch.location(in: self)) }  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {      // Add new points to the path     let touch: UITouch = touches.first!      path?.addLine(to: touch.location(in: self))     self.setNeedsDisplay() }  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {      let touch = touches.first      path?.addLine(to: touch!.location(in: self))     self.setNeedsDisplay()     let annotation = PDFAnnotation(bounds: self.bounds, forType: .ink, withProperties: nil)     annotation.add(self.path!)     delegate?.addAnnotation(annotation) }  override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {     self.touchesEnded(touches, with: event) }  override func draw(_ rect: CGRect) {     // Draw the path     path?.stroke() }  override init(frame: CGRect) {     super.init(frame: frame)     self.isMultipleTouchEnabled = false } }


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!