PencilKit visionOS附件在模拟器可用,Vision Pro真机无法绘制
Vision Pro Immersive空间中PKCanvasView无法绘制的原因及修复
问题原因
- 无碰撞检测:承载画布的RealityKit实体没有添加碰撞组件,Vision Pro的手势射线无法"命中"3D空间中的画布视图,导致输入事件根本传不到PKCanvasView。
- 第一响应者失效:Immersive空间的Attachment环境下,直接在
makeUIView中调用canvas.becomeFirstResponder()时机不对,画布无法获取输入焦点,接收不到绘制事件。 - Canvas实例管理错误:把PKCanvasView作为
@State变量从外部传入UIViewRepresentable,视图刷新时容易导致实例状态异常,干扰输入处理逻辑。 - 输入适配问题:PKCanvasView默认针对2D触摸/Apple Pencil优化,Vision Pro的3D空间手势需要延迟激活第一响应者,确保视图完全挂载后再处理输入。
修复方案
1. 给Attachment实体添加碰撞体
在RealityView中为承载画布的实体添加碰撞组件,让手势能识别到画布的位置:
struct ImmersiveView: View { var body: some View { RealityView { content, attachments in let entity = Entity() entity.position = [0.0, 1.0, -1.0] // 添加适配画布尺寸的碰撞盒(可根据实际画布大小调整) entity.components.set(CollisionComponent(shapes: [.generateBox(size: [0.6, 0.4, 0.01])])) if let attachment = attachments.entity(for: "draw") { entity.addChild(attachment) } content.add(entity) } attachments: { Attachment(id: "draw") { GraffitiView() } } } }
2. 修正Canvas实例创建与第一响应者逻辑
不在外部用@State保存Canvas实例,改在UIViewRepresentable内部创建,并延迟激活第一响应者:
struct GraffitiView: View { @State private var color: Color = .orange @State private var pencilType: PKInkingTool.InkType = .crayon var body: some View { DrawingView(pencilType: $pencilType, color: $color) .frame(width: 600, height: 400) // 明确画布尺寸,方便碰撞体匹配 } } struct DrawingView: UIViewRepresentable { @Binding var pencilType: PKInkingTool.InkType @Binding var color: Color var ink: PKInkingTool { PKInkingTool(pencilType, color: UIColor(color)) } func makeUIView(context: Context) -> PKCanvasView { let canvas = PKCanvasView() canvas.drawingPolicy = .anyInput canvas.tool = ink canvas.backgroundColor = .black canvas.alwaysBounceVertical = false // 延迟执行,确保视图已挂载到3D空间 DispatchQueue.main.async { canvas.becomeFirstResponder() let toolPicker = PKToolPicker.shared() toolPicker.setVisible(true, forFirstResponder: canvas) toolPicker.addObserver(canvas) } return canvas } func updateUIView(_ uiView: PKCanvasView, context: Context) { uiView.tool = ink } }
3. 额外优化建议
- 关闭尺子功能(
isRulerActive = false),避免尺子的交互干扰画布绘制。 - 给GraffitiView设置明确的frame,让碰撞体尺寸和画布实际大小匹配,提升手势命中精度。
内容的提问来源于stack exchange,提问作者Alejandro Wurm
相关产品推荐
相关产品推荐

