充分发挥 Apple Pencil 的能力
Squeeze the most out of Apple Pencil
2024年6月10日
一句话判断
PKToolPicker 今年开放了自定义工具的能力,你的 App 终于可以把画笔、橡皮、印章等自定义绘制工具直接嵌入系统工具栏,和 PencilKit 原生工具混排。
这场 Session 讲了什么
PencilKit 的工具栏(PKToolPicker)在 iOS 18 和 visionOS 2 上迎来了最大的架构变化。之前工具栏只支持 PencilKit 内置的画笔、橡皮、套索等工具,你的自定义绘制功能只能放在工具栏外面。现在你可以通过 PKToolPickerCustomItem 把自己的工具塞进工具栏,获得和原生工具一样的颜色选择器、粗细滑块、squeeze 唤起等系统能力。
Session 同时覆盖了 Apple Pencil Pro 的新特性集成:squeeze 手势可以在悬停位置弹出工具栏,barrel roll(笔杆旋转)可以控制笔触角度,悬停预览会显示工具的精确阴影。这些能力对 PencilKit 画布是自动支持的,对自定义画布也有对应的 API。
值得深挖的点
PKToolPickerCustomItem:工具栏终于开了口子
PKToolPickerCustomItem 是今年 PencilKit 最实质的更新。它让你可以定义一个完全自定义的工具项,指定它支持哪些属性(颜色、粗细、不透明度),PencilKit 会为这些属性提供标准的 UI 控件。如果你需要更多自定义属性(比如印章形状选择),可以提供一个 UIViewController 作为额外的控制面板。
工具图标通过一个闭包实时生成,PencilKit 在颜色、粗细、不透明度变化时自动调用这个闭包获取新图标。用 UILabel.drawText(in:) 在图标上绘制数值指示器(如不透明度百分比)是一个实用的小技巧。当自定义属性变化时,需要手动调用 reloadImage() 刷新图标。
当用户选中自定义工具时,所有观察工具栏的 PKCanvasView 会自动关闭绘制——因为现在由你的代码负责渲染。这是一种”渲染和选择分离”的设计:PencilKit 管工具选择 UI,你的 App 管绘制逻辑。这种解耦让工具栏可以同时服务于 PencilKit 画布和自定义画布。
Apple Pencil Pro 的交互深度
Apple Pencil Pro 带来了三个新交互维度:squeeze、barrel roll、增强的 hover。Squeeze 会在悬停位置弹出工具栏,这对大画布场景特别有用——用户不需要移到手势区切换工具。Barrel roll 让笔触角度跟随笔杆旋转,PencilKit 的记号笔和钢笔已经自动支持。
对自定义画布的开发者来说,hover 事件提供了精确的位置和工具信息,你可以据此渲染工具预览。Session 展示了一个印章工具如何利用 barrel roll 控制印章旋转角度,配合 squeeze 快速切换工具,整个绘制流程非常连贯。
代码片段
定义自定义工具项
// 创建一个印章工具的自定义工具项
let config = PKToolPickerCustomItem.Configuration(
color: .black,
width: 20,
opacity: 1.0
)
// 支持颜色和粗细,提供一个额外的视图控制器控制印章形状
let stampItem = PKToolPickerCustomItem(
configuration: config,
additionalControls: stampShapeViewController,
imageProvider: { updatedConfig in
// 根据当前配置生成工具图标
let image = UIGraphicsImageRenderer(size: CGSize(width: 44, height: 44)).image { ctx in
// 绘制当前印章形状的缩略图
drawStampPreview(in: ctx.format.bounds, color: updatedConfig.color)
}
return image
}
)
场景:把自定义印章工具放进 PKToolPicker。坑:imageProvider 闭包在主线程执行,如果图标生成逻辑太重会造成卡顿。
配置工具栏的工具集
// iPadOS 18 新增:自定义工具栏的工具列表
let toolItems: [PKToolPickerItem] = [
PKToolPickerItem.inkTool(.pen, color: .black, width: 1),
PKToolPickerItem.inkTool(.pen, color: .red, width: 1), // 同类型可以多个
PKToolPickerItem.eraserTool(.bitmap),
PKToolPickerItem.rulerItem,
stampItem // 自定义工具混排
]
let picker = PKToolPicker(toolItems: toolItems)
picker.addObserver(canvasView)
场景:提供精简的工具集,只显示 App 需要的工具。坑:PKToolPickerItem.inkTool 同类型可以添加多个(比如两支不同颜色的笔),但要注意用户辨识。
隐藏 Object Reticle 进入 Area 模式
// 跳过物体检测步骤,直接进入区域捕获模式
let captureView = ObjectCaptureView()
.hideObjectReticle // 隐藏边界框指示器
// 不调用 startDetecting,直接调用 startCapturing 进入 area mode
场景:Object Capture 的区域模式不需要边界框。坑:这个 API 属于 Object Capture 而非 PencilKit,但 Session 提到了它,容易混淆。
最佳实践
新项目: 如果你的 App 有绘制功能,优先评估 PencilKit + 自定义工具的方案是否能覆盖需求。PencilKit 免费提供 squeeze、hover、barrel roll 支持,自己从零实现这些交互的成本很高。只有在 PencilKit 渲染管线不满足需求时(比如需要 GPU 加速的自定义渲染),才考虑完全自定义画布。
已有项目: 如果你已经用 PencilKit 但把自定义工具放在了工具栏外面,今年应该把它们迁移到 PKToolPickerCustomItem 中。迁移后你的自定义工具自动获得 Apple Pencil Pro 的全部交互能力,不需要额外代码。已有画布代码不需要改动。
还有什么值得关注
PKToolPicker新增了accessoryBarButtonItems属性,可以在工具栏尾部添加 UIBarButtonItem,适合放”插入文本框”、“设置背景”等非绘制操作。- Scribble 工具现在可以作为工具栏项添加,统一手写识别和绘制工具的切换入口。
- 在 visionOS 上,工具栏以 ornament 形式悬浮在窗口外,squeeze 可以在任何位置唤起。