What's new in PDFKit
System & Services 进阶 20m

PDFKit 的新变化

What's new in PDFKit

2022年6月6日

在 Apple 官方观看视频

一句话判断

Live Text OCR、表单自动识别和 PencilKit 叠加视图——PDFKit 在 iOS 16 上的更新让 PDF 应用从”只能看”变成了”能交互”。

这场 Session 讲了什么

PDFKit 在 iOS 16 和 macOS Ventura 中获得了三项主要更新:

Live Text(实况文本):即使是扫描的位图 PDF(不包含任何文本层),用户也可以直接选择和搜索其中的文字。OCR 按需执行——只在用户与某个页面交互时才处理该页,不会在打开文档时一次性处理所有页面。处理结果可以就地保存到文档中。

表单识别增强:PDF 表单字段(如文本输入框)会被自动识别,即使原始文档没有内置交互字段。用户可以用 Tab 键在字段间跳转,像填网页表单一样填写 PDF 表单。

叠加视图(Overlay Views):新的 PDFPageOverlayViewProvider 协议让你可以在每个 PDF 页面上叠加自定义视图。Session 用 PencilKit 做了示范——在 PDF 页面上叠加 PKCanvasView,用户可以直接用 Apple Pencil 在 PDF 上标注。标注数据在页面滚出视图时自动保存。

此外还有从图片创建 PDF 页面的新 API,支持设置页面大小、旋转方向和是否放大小图。

值得深挖的点

Live Text 的按需处理策略体现了性能意识。一个 PDF 文档可能有数百甚至数千页,如果在打开时全部做 OCR 会导致严重的延迟和资源消耗。PDFKit 的策略是”用到哪页处理哪页”——用户滚动到某页并尝试选择文本时才触发 OCR。结果还可以保存到文档中,下次打开不需要重新识别。

Overlay View 的生命周期管理解决了”千页 PDF 上创建视图”的问题。PDFKit 已经有智能的页面预加载机制(在用户滚动前提前准备内容)。Overlay View Provider 挂载到这个机制上——当页面即将显示时调用 overlayViewForPage,当页面滚出视图时调用 willEndDisplayingOverlayView。你不需要自己管理视图的创建和回收时机。

PencilKit 集成的代码量惊人地少。Session 中完整的 PencilKit overlay 实现(包括数据保存和恢复)只有大约 30 行代码。核心模式是:在 willEndDisplayingOverlayView 中把 PKDrawing 数据存到自定义 PDFPage 子类的属性上,下次页面重新显示时再恢复。

代码片段

实现 PencilKit 叠加视图:

// 自定义 PDFPage 子类,存储绘图数据
class DrawingPage: PDFPage {
    var drawing: PKDrawing?
}

// 实现 Overlay View Provider
class DrawingProvider: NSObject, PDFPageOverlayViewProvider {
    // 页面到视图的映射
    private var pageToView = [PDFPage: PKCanvasView]()
    
    func overlayView(for page: PDFPage) -> UIView? {
        // 如果已有视图就复用,否则创建新的
        if let existingView = pageToView[page] {
            return existingView
        }
        
        let canvasView = PKCanvasView()
        // 恢复之前的绘图数据
        if let drawingPage = page as? DrawingPage {
            canvasView.drawing = drawingPage.drawing ?? PKDrawing()
        }
        pageToView[page] = canvasView
        return canvasView
    }
    
    func willEndDisplayingOverlayView(_ view: UIView, for page: PDFPage) {
        // 页面滚出视图时保存绘图数据
        guard let canvasView = view as? PKCanvasView,
              let drawingPage = page as? DrawingPage else { return }
        drawingPage.drawing = canvasView.drawing
    }
}

从图片创建 PDF 页面:

// 使用新的便捷 API
let page = PDFPage(image: cgImage)!
// 或使用更详细的配置
let options: [PDFPageImageCreationOptions: Any] = [
    .mediaBox: CGRect(x: 0, y: 0, width: 612, height: 792), // Letter 尺寸
    .rotation: 0,  // 纵向
    .upscaleIfSmaller: true  // 小图自动放大
]

注册 Overlay Provider:

// 在 PDFView 上注册 provider
pdfView.pageOverlayViewProvider = drawingProvider
// PDFKit 会在合适的时机自动调用 provider 的方法

最佳实践

  • Live Text 不需要额外代码:PDFView 自动支持,只需确保使用 iOS 16+
  • Overlay 视图要轻量创建:PDFKit 可能频繁调用创建和销毁,不要在里面做重操作
  • 在 willEndDisplaying 中保存数据:这是保存用户编辑内容的正确时机
  • PDFPage 子类存绘图数据:用自定义属性存储 PKDrawing,避免每次都从文件读取
  • 从图片创建 PDF 用 CGImage:避免不必要的格式转换,直接用 CoreGraphics 原生类型

还有什么值得关注

  • PDFKit 的四大核心类是 PDFView、PDFDocument、PDFPage、PDFAnnotation
  • Session 建议搭配 “Introducing PDFKit” 基础教程一起看
  • 表单识别不需要额外配置,系统自动处理
  • Overlay View 同时支持 iOS(UIView)和 macOS(NSView)
WWDC 2022