使用 SwiftUI 实现 PencilKit 撤消功能

Poi*_*urn 1 swiftui uiviewrepresentable pencilkit

编辑:感谢一些反馈,我已经能够部分工作(更新代码以反映当前更改)。

即使该应用程序似乎按预期工作,我仍然收到“正在修改状态...”警告。如何updateUIViewcanvasViewDrawingDidChange不导致此问题的情况下更新视图的绘图并将新绘图推入堆栈?我尝试将它包装在调度调用中,但这只会创建一个无限循环。


我正在尝试在UIViewRepresentable( PKCanvasView) 中实现撤消功能。我有一个父 SwiftUI 视图WriterView,其中包含两个按钮和画布。

这是父视图:

struct WriterView: View {
    @State var drawings: [PKDrawing] = [PKDrawing()]

    var body: some View {
        VStack(spacing: 10) {
            Button("Clear") {
                self.drawings = []
            }
            Button("Undo") {
                if !self.drawings.isEmpty {
                    self.drawings.removeLast()
                }
            }
            MyCanvas(drawings: $drawings)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我如何实施我的UIViewRepresentable

struct MyCanvas: UIViewRepresentable {
    @Binding var drawings: [PKDrawing]

    func makeUIView(context: Context) -> PKCanvasView {
        let canvas = PKCanvasView()
        canvas.delegate = context.coordinator
        return canvas
    }

    func updateUIView(_ uiView: PKCanvasView, context: Context) {
        uiView.drawing = self.drawings.last ?? PKDrawing()
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self._drawings)
    }

    class Coordinator: NSObject, PKCanvasViewDelegate {
        @Binding drawings: [PKDrawing]

        init(_ drawings: Binding<[PKDrawing]>) {
            self._drawings = drawings
        }

        func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
            drawings.append(canvasView.drawing)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

[SwiftUI] 在视图更新期间修改状态,这将导致未定义的行为。

大概是由我的协调员的 do change 功能引起的,但我不知道如何解决这个问题。解决这个问题的最佳方法是什么?

谢谢!

Poi*_*urn 6

我终于(偶然地)想出了如何使用 UndoManager 做到这一点。我仍然不确定这到底为什么有效,因为我从不需要调用self.undoManager?.registerUndo(). 如果您理解为什么我不必注册活动,请发表评论。

这是我的工作父视图:

struct Writer: View {
    @Environment(\.undoManager) private var undoManager
    @State private var canvasView = PKCanvasView()
    
    var body: some View {
        VStack(spacing: 10) {
            Button("Clear") {
                canvasView.drawing = PKDrawing()
            }
            Button("Undo") {
                undoManager?.undo()
            }
            Button("Redo") {
                undoManager?.redo()
            }
            MyCanvas(canvasView: $canvasView)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的工作子视图:

struct MyCanvas: UIViewRepresentable {
    @Binding var canvasView: PKCanvasView
    
    func makeUIView(context: Context) -> PKCanvasView {
        canvasView.drawingPolicy = .anyInput
        canvasView.tool = PKInkingTool(.pen, color: .black, width: 15)
        return canvasView
    }

    func updateUIView(_ canvasView: PKCanvasView, context: Context) { }
}
Run Code Online (Sandbox Code Playgroud)

这当然感觉更像是 SwiftUI 的预期方法,并且肯定比我之前所做的尝试更优雅。