在iOS 11 PDFKit文档上实现墨迹注释

时间:2017-11-06 11:19:16

标签: ios pdf touch ios11

我想允许用户在PDFView中查看的iOS 11 PDFKit文档上绘图。图纸最终应嵌入PDF中。

后者我通过添加类型" ink"的PDFAnnotation解决了使用与用户绘图相对应的UIBezierPath到PDFPage。

但是,如何实际记录用户在PDFView上创建的触摸以创建这样的UIBezierPath?

我已尝试在PDFView和PDFPage上覆盖touchesBegan,但它永远不会被调用。我试过添加一个UIGestureRecognizer,但没有做任何事情。

我假设我需要事后使用PDFView实例方法convert(_ point:CGPoint,to page:PDFPage)将获得的坐标转换为适合注释的PDF坐标。

3 个答案:

答案 0 :(得分:10)

最后,我通过创建扩展UIViewController和UIGestureRecognizerDelegate的PDFViewController类解决了这个问题。我添加了一个PDFView作为子视图,并将一个UIBarButtonItem添加到navigationItem,用于切换注释模式。

我在名为signingPath的UIBezierPath中记录触摸,并使用以下代码在currentAnnotation中使用类型PDFAnnotation的当前注释:

 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)
    }
}

注释切换按钮只运行:

pdfView.isUserInteractionEnabled = !pdfView.isUserInteractionEnabled

这确实是它的关键,因为这会禁用PDF上的滚动并使我能够接收触摸事件。

触摸事件被记录并立即转换为PDFAnnotation的方式意味着在PDF上书写时注释是可见的,并且它最终被记录到PDF中的正确位置 - 无论滚动位置如何。

确保它最终出现在右页只是类似地将页面编号的硬编码0更改为pdfView.page(for:position,nearest:true)值。

答案 1 :(得分:5)

我已经通过创建一个新的视图类(例如Annotate View)并在用户注释时置于PDFView之上来完成此任务。

此视图使用它的默认touchesBegan / touchesMoved / touchesEnded方法在手势后创建贝塞尔曲线路径。触摸结束后,我的视图会将其作为注释保存在pdf上。

注意:您需要一种方法让用户决定他们是否处于注解状态。

我的班级

streamA.pipe(streamB, {end: false}).pipe(streamC, {end: false})

我的注释视图

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)
}
}

答案 2 :(得分:3)

除了jksoegaard的出色回答外,还对像我这样的新手做了一些澄清:

  1. 您需要在项目中包含UIBezierPath + .swift,才能识别.moveCenter和rect.center。从https://github.com/xhamr/paintcode-path-scale下载。
  2. 这些行可以排除:

    UIGraphicsBeginImageContext(CGSize(width: 800, height: 600))
    

    let page = pdfView.page(for: position, nearest: true)!
  1. 您需要在函数之外声明一些全局变量:

    var signingPath = UIBezierPath()
    var annotationAdded : Bool?
    var lastPoint : CGPoint?
    var currentAnnotation : PDFAnnotation?
    
  2. 最后,如果要使墨水更宽且颜色更好,则需要做两件事:

a。每次看到annotation.add或currentAnnotation.add之前,都需要(根据功能需要使用annotation或currentAnnotation):

    let b = PDFBorder()
    b.lineWidth = { choose a pixel number here }
    currentAnnotation?.border = b
    currentAnnotation?.color=UIColor.{ your color of choosing }

我建议为颜色指定一个较低的alpha。结果很漂亮,并且受笔画速度的影响。例如,红色为:

    UIColor(red: 255/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.1)

b。记录每一次触摸的区域都需要容纳较粗的线。代替

    let rect = signingPath.bounds

尝试以10px的厚度为例:

    let rect = CGRect(x:signingPath.bounds.minX-5, 
    y:signingPath.bounds.minY-5, width:signingPath.bounds.maxX- 
    signingPath.bounds.minX+10, height:signingPath.bounds.maxY- 
    signingPath.bounds.minY+10)

重要:touchesEnded函数还使用currentAnnotation变量。您还必须在该函数中重复rect的定义(上面的简短描述或我建议的一个),并在其中也重复currentAnnotation的定义:

    currentAnnotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)

如果不这样做,单击不动的水龙头会使您的应用崩溃。

我可以验证文件一旦保存,便保留了注释。保存的示例代码:

    let pdfData = pdfDocument?.dataRepresentation()
    let annotatedPdfUrl = URL(fileURLWithPath: "\ 
    (NSSearchPathForDirectoriesInDomains(.documentsDirectory, .userDomainMask, 
        true)[0])/AnnotatedPDF.pdf")
    try! pdfData!.write(to: annotatedPdfUrl)