SwiftUI封装PKCanvasView后可编辑状态切换失效如何解决
核心问题说明
SwiftUI 桥接UIKit视图的UIViewRepresentable协议有明确的生命周期规则:
makeUIView(context:)仅在视图首次被挂载到视图树时调用一次,后续任何状态变更都不会再次触发该方法- 当SwiftUI侧的绑定属性更新、父视图重绘时,系统会自动调用
updateUIView(_:context:)方法,这才是同步原生视图状态的唯一入口
当前代码将所有状态相关的逻辑都写在了makeUIView中,且updateUIView为空实现,自然不会在可编辑状态切换时产生任何视图更新。
此外代码中还存在两个会阻碍功能实现的错误:
- 给CanvasView加了
.allowsHitTesting(false),会直接拦截所有触摸事件,让画布完全无法接收输入 - 在非编辑状态下设置
isUserInteractionEnabled = false,会同时屏蔽Apple Pencil和手指的输入,不符合“非编辑状态依然支持Apple Pencil绘图、手指手势留给其他交互”的需求
具体修复方案
1. 重写CanvasView的状态同步逻辑
将所有依赖editable状态变化的逻辑从makeUIView中移出,放到updateUIView中执行,修正后的完整实现如下:
struct CanvasView: UIViewRepresentable { @Binding var canvasView: PKCanvasView @Binding var editable: Bool func makeUIView(context: Context) -> PKCanvasView { // 仅做一次的初始化配置 canvasView.tool = PKInkingTool(.pen, color: .gray, width: 10) canvasView.backgroundColor = .clear canvasView.isOpaque = false // 初始化ToolPicker的关联 let toolPicker = PKToolPicker.shared toolPicker.addObserver(canvasView) canvasView.becomeFirstResponder() return canvasView } func updateUIView(_ uiView: PKCanvasView, context: Context) { // 状态变更时的同步逻辑 // 配置输入策略:编辑状态响应所有输入,非编辑状态仅响应Apple Pencil uiView.drawingPolicy = editable ? .anyInput : .pencilOnly // 同步工具条显隐 PKToolPicker.shared.setVisible(editable, forFirstResponder: uiView) } // 视图销毁时清理观察者,避免内存泄漏 static func dismantleUIView(_ uiView: PKCanvasView, coordinator: Coordinator) { PKToolPicker.shared.removeObserver(uiView) } }
2. 修正ContentView的配置错误
删除不必要的交互拦截配置,核心调整点:
- 移除CanvasView外层的
.allowsHitTesting(false)修饰,保证画布能正常接收输入 - 移除手动设置
isUserInteractionEnabled的逻辑,该需求完全由drawingPolicy实现即可
修正后的核心视图片段:
CanvasView(canvasView: $canvasView, editable: self.$isDrawing) .navigationBarTitle(Text("Drawing Demo"), displayMode: .inline) .navigationBarItems(trailing: Button(action: toggleIsDrawing) { Image(systemName: "pencil.tip.crop.circle") })
关键注意点
drawingPolicy是控制输入源的核心属性,无需额外调整交互开关:.anyInput状态下,手指和Apple Pencil的触摸都会被识别为绘图输入.pencilOnly状态下,只有Apple Pencil的触摸会被识别为绘图输入,手指触摸会正常穿透到外层视图,供其他交互逻辑使用
- ToolPicker的显隐必须放在
updateUIView中触发,放在初始化方法中仅会在第一次加载时生效一次,无法跟随状态切换更新 - 视图销毁时记得移除ToolPicker的观察者,避免循环引用导致的内存泄漏
内容的提问来源于stack exchange,提问作者Ramy Al Zuhouri
相关产品推荐
相关产品推荐

