问题
I'm trying to use PencilKit
in SwiftUI.
- How can I detect in the
updateUIView
-function, whichBinding
-variable was updated? For example I don't want to clear the canvas when changing the color. - And is there a better way to clear the canvas than toggling a boolean? Toggling a boolean forces the
updateUIView
-function to execute.
import SwiftUI
import UIKit
import PencilKit
struct ContentView: View {
@State var color = UIColor.black
@State var clear = false
var body: some View {
VStack{
PKCanvas(color: $color, clear:$clear)
VStack(){
Button("Change to BLUE"){ self.color = UIColor.blue }
Button("Change to GREEN"){ self.color = UIColor.green }
Button("Clear Canvas"){ self.clear.toggle() }
}
}
}
}
struct PKCanvas: UIViewRepresentable {
class Coordinator: NSObject, PKCanvasViewDelegate {
var pkCanvas: PKCanvas
init(_ pkCanvas: PKCanvas) {
self.pkCanvas = pkCanvas
}
}
@Binding var color:UIColor
@Binding var clear:Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> PKCanvasView {
let canvas = PKCanvasView()
canvas.tool = PKInkingTool(.pen, color: color, width: 10)
canvas.delegate = context.coordinator
return canvas
}
func updateUIView(_ canvasView: PKCanvasView, context: Context) {
// clears the canvas
canvasView.drawing = PKDrawing()
// sets a new color
canvasView.tool = PKInkingTool(.pen, color: color, width: 10)
}
}
回答1:
This will do the trick:
func updateUIView(_ canvasView: PKCanvasView, context: Context) {
if clear != context.coordinator.pkCanvas.clear{
canvasView.drawing = PKDrawing()
}
canvasView.tool = PKInkingTool(.pen, color: color, width: 10)
}
回答2:
I observed similar pattern many times, and here we get intercommunication between SwiftUI-value-declarative-state-managed
world and xKit-OOP-imperative-action-managed
world... and there are attempts either to put everything from one to another, or vice versa...
I've got a mind that probably it would be better to keep each nature for each and have some mediator or actor between two... so for your use-case I'd think that I would go different way, say (scratchy, not tested, for consideration):
struct ContentView: View {
//@State var color = UIColor.black // < both these have nothing to ContentView
//@State var clear = false
let pkActor = PKCanvasActor() // < mediator, reference type
var body: some View {
VStack{
PKCanvas(actor: pkActor)
VStack(){
Button("Change to BLUE"){ self.pkActor.use(color: UIColor.blue) }
Button("Change to GREEN"){ self.pkActor.use(color: UIColor.green) }
Button("Clear Canvas"){ self.pkActor.clear() }
}
}
}
}
somewhere here
func makeUIView(context: Context) -> PKCanvasView {
let canvas = PKCanvasView()
self.actor.canvas = canvas // but think to avoid cycle reference
and pure-OOP part
class PKCanvasActor {
var canvas: PKCanvasView
func use(color: Color) {
// do anything with canvas color
}
func clear() {
canvas.drawing = PKDrawing()
}
// ... any more actions
}
Of course there simple approach I proposed for similar scenario in SwiftUI Button interact with Map... but above way looks more preferable for me.
Remark: one might say the Coordinator is for this purpose, but its life-time is managed by SwiftUI-internals and it participate in those internal workflows, so I'd avoid intruding into those relationships...
来源:https://stackoverflow.com/questions/59430099/pencilkit-in-swiftui