appfrosch
appfrosch

Reputation: 1366

`PKCanvasViewDelegate.canvasViewDidFinishRendering(_:)` not called

I implemented a drawing area in one of my apps using PencilKit via UIViewRepresentable. As my app is already syncing via iCloud, I want to make sure that drawings appear on different device sizes properly.

Right now, this scribble created on the iPad … iPad Screenshot of a doodle

… looks like this on the iPhone: iPhone Screenshot from that same doodle, just cropped and unscaled

I already do know that I can set the scale of a PKDrawing with PKDrawing.transform(using:).

To be able to do that, I need to know the container size of the drawing that the UIViewRepresentable is rendering. I thought that I could use PKCanvasViewDelegate.canvasViewDidFinishRendering to get the size back and to scale the drawing are.

Right now, my biggest issue is that above assumption does not hold true–PKCanvasViewDelegate.canvasViewDidFinishRendering is currently not called and I don't know why … I know that cause I have set a breakpoint where Xcode won't stop and I have OSLog statements in all the delegate funcs …

So basically, I am looking for

The scaling part is this:

extension PKDrawing {
  mutating func scale(in frame: CGSize) {
    AppLogger.doodle.debug("Frame is: \(frame.debugDescription)")
    var scaleFactor:CGFloat = 0

    if self.bounds.width != frame.width {
      scaleFactor = frame.width / self.bounds.width
    } else if self.bounds.height != frame.height {
      scaleFactor = frame.height / self.bounds.height
    }

    let transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)

    self.transform(using: transform)
  }
}

The UIViewRepresentable is this:

import PencilKit

struct PKViewRepresentable: UIViewRepresentable {
  @Binding var drawing: PKDrawing
  var toolpicker = PKToolPicker()

  func makeUIView(context: Context) -> PKCanvasView {
    AppLogger.doodle.debug("Called #file: \(#file), #function: \(#function), #line: \(#line)")
    let canvasView = PKCanvasView()
    canvasView.drawing = drawing
    canvasView.overrideUserInterfaceStyle = .light
    canvasView.drawingPolicy = .anyInput
    canvasView.tool = PKInkingTool(.pen, color: .black, width: 15)
    toolpicker.setVisible(true, forFirstResponder: canvasView)

    toolpicker.addObserver(canvasView)
    canvasView.becomeFirstResponder()
    canvasView.delegate = context.coordinator
    return canvasView
  }

  func updateUIView(_ uiView: PKCanvasView, context: Context) {
    AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
  }

  func makeCoordinator() -> Coordinator {
    AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    return Coordinator(self)
  }

  class Coordinator: NSObject, PKCanvasViewDelegate {
    let parent: PKViewRepresentable

    init(_ parent: PKViewRepresentable) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
      self.parent = parent
    }

    func canvasViewDidFinishRendering(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    }

    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
      self.parent.drawing = canvasView.drawing
    }

    func canvasViewDidBeginUsingTool(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    }

    func canvasViewDidEndUsingTool(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    }
  }
}

Here's all the code in a little sample app:

import SwiftUI

@main
struct DrawingSampleApp: App {
    var body: some Scene {
        WindowGroup {
          DrawingView()
        }
    }
}

struct DrawingView: View {
  @State private var title: String = "Some fancy drawing"
  @State private var drawing: PKDrawing = .init()

  var body: some View {
    VStack {
      TextField("Title", text: $title)
        .textFieldStyle(.roundedBorder)
        .padding()
      Divider()
      PKViewRepresentable(drawing: $drawing)
        .padding()
    }
  }
}

#Preview {
  DrawingView()
}

import PencilKit

extension PKDrawing {
  mutating func scale(in frame: CGSize) {
    AppLogger.doodle.debug("Frame is: \(frame.debugDescription)")
    var scaleFactor:CGFloat = 0

    if self.bounds.width != frame.width {
      scaleFactor = frame.width / self.bounds.width
    } else if self.bounds.height != frame.height {
      scaleFactor = frame.height / self.bounds.height
    }

    let transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)

    self.transform(using: transform)
  }
}

struct PKViewRepresentable: UIViewRepresentable {
  @Binding var drawing: PKDrawing
  var toolpicker = PKToolPicker()

  func makeUIView(context: Context) -> PKCanvasView {
    AppLogger.doodle.debug("Called #file: \(#file), #function: \(#function), #line: \(#line)")
    let canvasView = PKCanvasView()
    canvasView.drawing = drawing
    canvasView.overrideUserInterfaceStyle = .light
    canvasView.drawingPolicy = .anyInput
    canvasView.tool = PKInkingTool(.pen, color: .black, width: 15)
    toolpicker.setVisible(true, forFirstResponder: canvasView)

    toolpicker.addObserver(canvasView)
    canvasView.becomeFirstResponder()
    canvasView.delegate = context.coordinator
    return canvasView
  }

  func updateUIView(_ uiView: PKCanvasView, context: Context) {
    AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
  }

  func makeCoordinator() -> Coordinator {
    AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    return Coordinator(self)
  }

  class Coordinator: NSObject, PKCanvasViewDelegate {
    let parent: PKViewRepresentable

    init(_ parent: PKViewRepresentable) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
      self.parent = parent
    }

    func canvasViewDidFinishRendering(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    }

    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
      self.parent.drawing = canvasView.drawing
    }

    func canvasViewDidBeginUsingTool(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    }

    func canvasViewDidEndUsingTool(_ canvasView: PKCanvasView) {
      AppLogger.doodle.debug("Called #function: \(#function), #line: \(#line)")
    }
  }
}

import OSLog

enum AppLogger {
  static let misc = Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: "misc")
  static let doodle = Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: "doodle")
}

Upvotes: 0

Views: 15

Answers (0)

Related Questions