首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用PencilKit实现SwiftUI撤消功能

使用PencilKit实现SwiftUI撤消功能
EN

Stack Overflow用户
提问于 2020-03-08 22:59:26
回答 5查看 2.9K关注 0票数 2

编辑:感谢一些反馈,我已经能够让这个部分工作(更新的代码来反映当前的变化)。

尽管该应用程序看起来像预期的那样工作,但我仍然得到“修改状态.”。警告。如何更新视图在updateUIView中的绘图,并在不引起此问题的情况下使用canvasViewDrawingDidChange将新的绘图推送到堆栈中?我尝试过将其封装在调度调用中,但这只是创建了一个无限循环。

我试图在UIViewRepresentable (PKCanvasView)中实现撤销功能。我有一个名为SwiftUI的父视图WriterView,它包含两个按钮和画布。

,这是父视图:

代码语言:javascript
复制
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)
        }
    }
}

下面是我如何实现我的UIViewRepresentable**:**

代码语言:javascript
复制
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)
        }
    }
}

我得到了以下错误:

在视图更新期间,

SwiftUI修改状态,这将导致未定义的行为。

据推测,这是由我的协调器的更改函数引起的,但我不知道如何解决这个问题。解决这个问题的最好方法是什么?

谢谢!

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2020-03-12 03:59:30

我终于(意外地)想出了如何使用UndoManager来完成这个任务。我仍然不清楚为什么会这样,因为我从来不需要打电话给self.undoManager?.registerUndo()。如果你明白我为什么不需要注册一个事件,请发表评论。

这里是我的工作父视图:

代码语言:javascript
复制
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)
        }
    }
}

,这是我的工作子视图:

代码语言:javascript
复制
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) { }
}

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

票数 7
EN

Stack Overflow用户

发布于 2020-03-12 06:42:16

为了完整起见,如果您想显示PKToolPicker,这里是我的UIViewRepresentable。

代码语言:javascript
复制
import Foundation
import SwiftUI
import PencilKit

struct PKCanvasSwiftUIView : UIViewRepresentable {

let canvasView = PKCanvasView()

#if !targetEnvironment(macCatalyst)
let coordinator = Coordinator()

class Coordinator: NSObject, PKToolPickerObserver {
    // initial values 
    var color = UIColor.black
    var thickness = CGFloat(30)

    func toolPickerSelectedToolDidChange(_ toolPicker: PKToolPicker) {
        if toolPicker.selectedTool is PKInkingTool {
            let tool = toolPicker.selectedTool as! PKInkingTool
            self.color = tool.color
            self.thickness = tool.width
        }
    }
    func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker) {
        if toolPicker.selectedTool is PKInkingTool {
            let tool = toolPicker.selectedTool as! PKInkingTool
            self.color = tool.color
            self.thickness = tool.width
        }
    }
}
func makeCoordinator() -> PKCanvasSwiftUIView.Coordinator {
    return Coordinator()
}
#endif

func makeUIView(context: Context) -> PKCanvasView {
    canvasView.isOpaque = false
    canvasView.backgroundColor = UIColor.clear
    canvasView.becomeFirstResponder()
    #if !targetEnvironment(macCatalyst)
    if let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first,
        let toolPicker = PKToolPicker.shared(for: window) {
        toolPicker.addObserver(canvasView)
        toolPicker.addObserver(coordinator)
        toolPicker.setVisible(true, forFirstResponder: canvasView)
    }
    #endif
    return canvasView
}

func updateUIView(_ uiView: PKCanvasView, context: Context) {

}
} 
票数 1
EN

Stack Overflow用户

发布于 2020-03-09 02:15:55

我认为错误可能来自私有func clearCanvas()和私有func undoDrawing()。试一试,看看它是否有效:

代码语言:javascript
复制
private func clearCanvas() {
 DispatchQueue.main.async {
    self.drawings = [PKDrawing()]
 }
}

undoDrawing()也是如此。

如果它来自canvasViewDrawingDidChange,那么做同样的技巧。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60592875

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档