PencilKit in SwiftUI

ぃ、小莉子 提交于 2020-01-06 04:51:52

问题


I'm trying to use PencilKit in SwiftUI.

  1. How can I detect in the updateUIView-function, which Binding-variable was updated? For example I don't want to clear the canvas when changing the color.
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!