You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PKCanvas莫名恢复原始绘图?技术问题求助

问题原因

你遇到的问题核心是错误地将PKCanvasView作为@State对象存储在SwiftUI视图中。PKCanvasView是UIKit的视图类,属于引用类型,而SwiftUI的@State设计用于存储值类型(如结构体、基本类型),用来管理UI状态的数据源,而非持有整个UIView实例。

当你调用load()设置canvasView.drawing = rendition后,SwiftUI的状态更新机制会触发CanvasView(UIViewRepresentable)的重新计算,但因为直接绑定了整个PKCanvasView实例,导致视图生命周期和状态不同步:后续绘制时,UIKit的画布实例和SwiftUI持有的@State实例之间出现数据不一致,最终导致绘图回退到之前的状态。

修复方案

正确的做法是让SwiftUI管理绘图数据(PKDrawing),而非UIView实例。重构代码如下:

1. 重构ContentView

将@State改为管理绘图数据,移除对PKCanvasView的持有:

struct ContentView: View {
    @State private var currentDrawing = PKDrawing()
    @State private var savedDrawing = PKDrawing()

    func save() {
        savedDrawing = currentDrawing
    }
    func load() {
        currentDrawing = savedDrawing
    }
    func delete() {
        currentDrawing = PKDrawing()
    }
    var body: some View {
        VStack {
            Button("Save") { save() }
            Button("Load") { load() }
            Button("Delete") { delete() }
            CanvasView(drawing: $currentDrawing)
                .frame(height: 400) // 给画布设置固定高度,确保可绘制区域
        }
        .padding()
    }
}

2. 重构CanvasView

让CanvasView接收PKDrawing的绑定,实现数据与UI的双向同步:

struct CanvasView: UIViewRepresentable {
    @Binding var drawing: PKDrawing
    @State private var toolPicker = PKToolPicker()

    func makeUIView(context: Context) -> PKCanvasView {
        let canvasView = PKCanvasView()
        canvasView.tool = PKInkingTool(.pen, color: .gray, width: 10)
        #if targetEnvironment(simulator)
        canvasView.drawingPolicy = .anyInput
        #endif
        canvasView.delegate = context.coordinator
        context.coordinator.canvasView = canvasView
        showToolPicker(for: canvasView)
        return canvasView
    }

    func updateUIView(_ uiView: PKCanvasView, context: Context) {
        // 当绑定的绘图数据变化时,同步更新画布内容
        if uiView.drawing != drawing {
            uiView.drawing = drawing
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(drawing: $drawing)
    }
}

// MARK: - 私有方法
private extension CanvasView {
    func showToolPicker(for canvasView: PKCanvasView) {
        toolPicker.setVisible(true, forFirstResponder: canvasView)
        toolPicker.addObserver(canvasView)
        canvasView.becomeFirstResponder()
    }
}

// MARK: - Coordinator
class Coordinator: NSObject, PKCanvasViewDelegate {
    @Binding var drawing: PKDrawing
    weak var canvasView: PKCanvasView? // 弱引用避免循环引用

    init(drawing: Binding<PKDrawing>) {
        _drawing = drawing
    }

    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
        // 画布内容变化时,同步更新绑定的绘图数据
        drawing = canvasView.drawing
    }
}

关键改动说明

  • 移除了对PKCanvasView的@State和绑定,改为管理PKDrawing数据,符合SwiftUI"数据驱动UI"的设计理念。
  • CanvasView的updateUIView方法负责在数据变化时同步画布内容,避免状态不一致。
  • Coordinator通过弱引用持有PKCanvasView,避免循环引用,同时在画布内容变化时更新绑定的drawing,确保SwiftUI状态和UIKit视图数据双向同步。

这样修改后,保存、加载、删除操作都会正确同步绘图数据,不会出现绘图回退的问题。

内容的提问来源于stack exchange,提问作者erotsppa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:43:09