Search code examples
iosswiftswiftuiuiviewrepresentablepencilkit

How to use PKToolPicker with PKCanvasView in SwiftUI


Currently, I am able to have a separate PKCanvasView and a PKToolPicker that is shown when a button is tapped on. However, the tool is not being transferred between the picker and the canvas view. Does anyone have any idea how to link the two such that when I change the tool in the picker, the tool is updated in the canvas view as well? I've attached my code below. Thank you!

import SwiftUI
import PencilKit

struct DrawingView: View {
    @State private var showPicker = false
    @State private var canvasView = PKCanvasView()
    var body: some View {
        VStack {
            PencilKitView(isActive: $showPicker, canvasView: $canvasView)
            Button("Picker") { self.showPicker.toggle() }
        }
    }
}

struct PencilKitView: UIViewRepresentable {
    typealias UIViewType = PKCanvasView
    @Binding var isActive: Bool
    @Binding var canvasView: PKCanvasView

    let coordinator = Coordinator()
    
    class Coordinator: NSObject, PKToolPickerObserver {
        
        func toolPickerSelectedToolDidChange(_ toolPicker: PKToolPicker) {
            // some code
        }
        func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker) {
            // some code
        }
        
    }
    
    func makeCoordinator() -> PencilKitView.Coordinator {
        return Coordinator()
    }
    
    func makeUIView(context: Context) -> PKCanvasView {
        canvasView
    }
    
    func updateUIView(_ uiView: PKCanvasView, context: Context) {
        uiView.isOpaque = true
        uiView.becomeFirstResponder()

        let toolPicker = PKToolPicker.init()
        toolPicker.addObserver(uiView)
        toolPicker.addObserver(coordinator)
        toolPicker.setVisible(isActive, forFirstResponder: uiView)
        
        DispatchQueue.main.async {
            uiView.becomeFirstResponder()
        }
    }
}


Solution

  • Solved it!

    import SwiftUI
    import PencilKit
    
    struct DrawingView: View {
        private var canvasView = PKCanvasView()
    
        var body: some View {
            MyCanvas(canvasView: canvasView)
        }
    }
    
    struct MyCanvas: UIViewRepresentable {
        var canvasView: PKCanvasView
        let picker = PKToolPicker.init()
        
        func makeUIView(context: Context) -> PKCanvasView {
            self.canvasView.tool = PKInkingTool(.pen, color: .black, width: 15)
            self.canvasView.becomeFirstResponder()
            return canvasView
        }
        
        func updateUIView(_ uiView: PKCanvasView, context: Context) {
            picker.addObserver(canvasView)
            picker.setVisible(true, forFirstResponder: uiView)
            DispatchQueue.main.async {
                uiView.becomeFirstResponder()
            }
        }
    }