Highlight
Apple 将设备端手写识别引擎
PKStrokeRecognizer开放给第三方开发者,支持 29 种语言,并新增 Bézier 路径互转、笔画切割和稳定 Stroke ID,让 PencilKit 从画板组件升级为可嵌入任意渲染管线的底层工具。
核心内容
手写识别:从系统独占到人人可用
在 iOS 27 之前,只有 Freeform 和 Notes 能用上 Apple 顶级的设备端手写识别。第三方 App 要么接入昂贵的第三方 SDK,要么用 Vision 框架自己拼凑,效果远不及系统原生。
现在 PKStrokeRecognizer 向所有开发者开放。它是个 Swift Actor,所有方法都是异步的,识别过程不会阻塞主线程。模型完全离线,随系统内置,不需要联网,也不消耗额外的存储空间。
(01:46)
PKStrokeRecognizer 提供三项核心能力:
- Recognized text:返回最可能的识别结果,适合实时显示用户写了什么
- Indexable content:返回所有候选词拼接成的字符串,适合 Spotlight 索引
- Search:在画布内搜索关键词并返回匹配区域的边界框
(03:41)
打破 PKCanvasView 的围墙
PencilKit 底层用三次均匀 B 样条(B-spline)存储笔画路径。这种表示方式对绘制很友好,但跟业界更常见的 Bézier 路径不兼容。
iOS 27 新增了路径转换 API,支持 PKStrokePath 和 Bézier 路径之间的无损互转。这意味着你可以把自己的渲染管线里的 Bézier 路径转成 PKStrokePath,再喂给 PKStrokeRecognizer 做识别,完全不需要 PKCanvasView。
(08:38)
更开放的绘图模型
iOS 27 给 PKStroke 和 PKStrokePath 增加了 Identifiable 协议支持,返回稳定的 UUID。Undo、Redo、变换之后,同一笔画的 ID 不变。
PKCanvasView 新增了 canvasViewSelectionDidChange 代理方法,选区变化时自动触发。renderGroupID 也从内部实现变成可控属性,可以手动控制哪些笔画被当作”未干墨水”进行混合渲染。
(10:32)
笔画切割
iOS 27 引入了两类切割 API:
- Programmatic erasing:用一段
PKStrokePath作为橡皮擦路径,程序化地切割笔画,效果跟用户在画布上用橡皮擦一样 - Substroke extraction:用参数化下标从笔画中提取任意片段,保持纹理粒子的一致性
切割操作在复杂绘图(几千个 Stroke)上开销较大,建议在后台线程执行。
(11:36)
详细内容
基础手写识别
(03:53)
import PencilKit
let recognizer = PKStrokeRecognizer()
await recognizer.updateDrawing(drawing)
myLabel.text = await recognizer.recognizedText()
关键点:
PKStrokeRecognizer是 Actor,线程安全updateDrawing和recognizedText都是异步方法- 默认使用设备语言识别,也可通过
preferredLanguages显式配置 - 支持 29 种语言,可通过
supportedLanguages查询 - Simulator 仅支持拉丁字符语言,中日韩等语言需真机测试
生成可索引内容
(05:22)
import PencilKit
let recognizer = PKStrokeRecognizer()
await recognizer.updateDrawing(drawing)
if let indexedContent = await recognizer.indexableContent {
index(text: indexedContent)
}
关键点:
indexableContent返回所有候选词拼接的字符串,适合 Spotlight 索引- 手写有歧义时(如 “1” 和 “l”),
recognizedText只给一个最佳答案,indexableContent给所有候选 recognizerVersion属性标识当前识别模型版本,建议跟索引内容一起持久化,加载时比对版本号决定是否重建索引- 每次笔画更新都调用识别器会耗电,建议做节流(throttle)
画布内搜索
(06:58)
import PencilKit
let recognizer = PKStrokeRecognizer()
await recognizer.updateDrawing(drawing)
let results = await recognizer.search("apple")
for result in results {
highlight(bounds: result.bounds)
}
关键点:
search返回SearchResult数组,每个结果包含匹配区域的bounds- 可结合
UIFindInteraction实现系统级的查找替换体验 - 搜索能力也为辅助功能提供了基础:VoiceOver 可以朗读手写内容,搜索让屏幕阅读器能定位到具体单词
核心启发
-
做儿童识字 App:用
PKStrokeRecognizer实时检查孩子写的是不是正确汉字。结合search判断笔画顺序,用 Substroke extraction 回放书写过程。入口 API:PKStrokeRecognizer.recognizedText()、PKStroke.subscript(_:)。 -
做支持手写的笔记 App:把用户的手写内容通过
indexableContent同步到 Core Spotlight,全文搜索覆盖手写笔记。入口 API:PKStrokeRecognizer.indexableContent、recognizerVersion。 -
做自定义画板:用 Metal 或 SwiftUI Canvas 做自己的渲染层,通过 Bézier 互转把笔画数据喂给
PKStrokeRecognizer。入口 API:PKStrokePath的 Bézier 转换方法。 -
做智能批注工具:用 Programmatic erasing API 实现”划掉删除”手势,用户划一条线穿过文字,自动识别并删除对应内容。入口 API:以
PKStrokePath作为橡皮擦路径的切割方法。 -
做手写公式计算器:识别用户手写的数学公式,转成 LaTeX 或直接用
PKStrokeRecognizer识别数字和运算符,计算结果。入口 API:PKStrokeRecognizer.preferredLanguages、recognizedText()。
关联 Session
- Meet PaperKit — PaperKit 构建在 PencilKit 之上,本 Session 的所有新 API 在 PaperKit 中同样可用
- SwiftUI — 学习如何在 SwiftUI 中集成 PencilKit 画布
- Swift Testing — 为手写识别逻辑编写测试
- UIKit modernization —
UIFindInteraction的系统查找体验集成
评论
GitHub Issues · utterances