如何获取.widget类型.text子类型PDFAnnotation内的textView并实现交互?
我之前做PDF编辑功能时刚好碰到过这个需求,折腾了一阵终于搞定了,分享下可行的实现方案,完全能达到iOS原生编辑文本注释的效果~
核心思路
PDFKit在你把.widget类型+.text子类型的注释添加到PDFPage后,会自动在PDFView中生成对应的UITextView控件,但这个控件并没有直接暴露给我们。所以我们的目标就是:找到这个隐藏的UITextView,让它成为第一响应者(弹出键盘),或者选中文本。
具体实现步骤
1. 先创建并添加文本注释
首先是常规的创建和添加注释的代码:
// 定义注释的位置和大小 let textAnnotationBounds = CGRect(x: 80, y: 150, width: 250, height: 45) // 创建.widget类型的文本注释 let textAnnotation = PDFAnnotation(bounds: textAnnotationBounds, forType: .widget, withProperties: nil) // 设置子类型为文本输入 textAnnotation.widgetFieldType = .text // 设置初始文本 textAnnotation.contents = "点击编辑我" // 添加到目标PDFPage(这里假设你已经获取到pdfPage实例) pdfPage.addAnnotation(textAnnotation)
2. 找到对应注释的UITextView
PDFView添加注释后需要一点时间渲染对应的UI控件,我们可以通过两种方式来捕获这个时机:
方式一:监听PDFView的注释添加通知(推荐)
注册PDFViewAnnotationAddedNotification,这样能精准捕获注释被添加到PDFView的时刻:
// 在视图初始化时注册通知 NotificationCenter.default.addObserver(self, selector: #selector(handleTextAnnotationAdded(_:)), name: .PDFViewAnnotationAdded, object: yourPDFView) // 处理通知的方法 @objc private func handleTextAnnotationAdded(_ notification: Notification) { // 先判断是不是我们要找的文本注释 guard let annotation = notification.userInfo?["PDFAnnotationNotificationKey"] as? PDFAnnotation, annotation.type == .widget, annotation.widgetFieldType == .text else { return } // 切换到主线程操作UI DispatchQueue.main.async { [weak self] in guard let self = self, let pdfView = self.yourPDFView else { return } // 递归遍历PDFView的子视图,找到对应注释的UITextView func findTargetTextView(for annotation: PDFAnnotation, in view: UIView) -> UITextView? { for subview in view.subviews { // 通过KVC访问私有属性,判断当前子视图是否对应目标注释 if let widgetView = subview as? NSObject, let attachedAnnotation = widgetView.value(forKey: "annotation") as? PDFAnnotation, attachedAnnotation == annotation { // 尝试直接获取textView(私有属性) if let textView = widgetView.value(forKey: "textView") as? UITextView { return textView } // 备用方案:遍历子视图找UITextView return subview.subviews.first(where: { $0 is UITextView }) as? UITextView } // 递归查找子视图 if let foundTextView = findTargetTextView(for: annotation, in: subview) { return foundTextView } } return nil } // 找到textView后激活它 if let targetTextView = findTargetTextView(for: annotation, in: pdfView) { // 弹出键盘 targetTextView.becomeFirstResponder() // 可选:自动选中所有文本,和原生编辑行为一致 targetTextView.selectedTextRange = targetTextView.textRange(from: targetTextView.beginningOfDocument, to: targetTextView.endOfDocument) } } }
方式二:延迟执行(简单但不够精准)
如果不想监听通知,也可以在添加注释后延迟一小段时间再查找控件(适合快速测试):
// 添加注释后延迟0.1秒执行(时间可根据实际情况调整) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in guard let self = self, let pdfView = self.yourPDFView else { return } // 调用上面的findTargetTextView函数查找并激活,代码和上面一致 if let targetTextView = self.findTargetTextView(for: textAnnotation, in: pdfView) { targetTextView.becomeFirstResponder() targetTextView.selectedTextRange = targetTextView.textRange(from: targetTextView.beginningOfDocument, to: targetTextView.endOfDocument) } }
3. 注意事项
- 主线程操作:所有UI相关的操作必须在主线程执行,避免崩溃。
- 私有API兼容:这里用到了KVC访问PDFKit的私有属性(
annotation和textView),虽然在大多数iOS版本中都稳定,但苹果可能在后续版本中修改这些属性名,建议做好兼容性判断(比如用if let而非强制解包)。 - 移除通知观察者:在视图销毁时记得移除通知观察者,避免内存泄漏:
deinit { NotificationCenter.default.removeObserver(self) }
这样实现后,添加文本注释后就会自动弹出键盘,并且可以直接编辑,完全和iOS原生编辑截图/PDF文本注释的行为一致~
内容的提问来源于stack exchange,提问作者crcalin
相关产品推荐
相关产品推荐

