Here is the implementation.
import SwiftUI
import PencilKit
struct DrawingCanvas_bug: UIViewRepresentable {
typealias UIViewType = PKCanvasView
private let toolPicker = PKToolPicker()
@Binding var drawing: PKDrawing
var isOpaque: Bool = true
var drawingDidChange: ((PKDrawing) -> Void)?
func makeUIView(context: Context) -> PKCanvasView {
let canvasView = PKCanvasView(frame: .zero)
canvasView.drawing = drawing
canvasView.delegate = context.coordinator
canvasView.backgroundColor = .clear
canvasView.isOpaque = isOpaque
canvasView.alwaysBounceVertical = true
// The size of the canvas is Zero, so I cannot update the ZoomScale now.
// context.coordinator.updateZoomScale(for: canvasView)
toolPicker.setVisible(true, forFirstResponder: canvasView)
toolPicker.addObserver(canvasView)
canvasView.becomeFirstResponder()
return canvasView
}
func updateUIView(_ canvasView: PKCanvasView, context: Context) {
DispatchQueue.main.async {
// We can get the correct ZoomScale and the correct ContentSize, but part of the drawing is not visible.
// Using the select tool can select them.
context.coordinator.updateZoomScale(for: canvasView)
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
static func dismantleUIView(_ canvasView: PKCanvasView, coordinator: Coordinator) {
canvasView.resignFirstResponder()
}
}
extension DrawingCanvas_bug {
class Coordinator: NSObject, PKCanvasViewDelegate {
var host: DrawingCanvas_bug
init(_ host: DrawingCanvas_bug) {
self.host = host
}
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
host.drawing = canvasView.drawing
if let action = host.drawingDidChange {
action(canvasView.drawing)
}
updateContentSizeForDrawing(for: canvasView)
}
func updateZoomScale(for canvasView: PKCanvasView) {
let canvasScale = canvasView.bounds.width / 768
canvasView.minimumZoomScale = canvasScale
canvasView.maximumZoomScale = canvasScale
canvasView.zoomScale = canvasScale
updateContentSizeForDrawing(for: canvasView)
}
func updateContentSizeForDrawing(for canvasView: PKCanvasView) {
let drawing = canvasView.drawing
let contentHeight: CGFloat
if !drawing.bounds.isNull {
contentHeight = max(canvasView.bounds.height, (drawing.bounds.maxY + 500) * canvasView.zoomScale)
} else {
contentHeight = canvasView.bounds.height
}
canvasView.contentSize = CGSize(width: 768 * canvasView.zoomScale, height: contentHeight)
}
}
}
And here is how I use:
NavigationSplitView(columnVisibility: .constant(.doubleColumn)) {
List(selection: $selection) {
ForEach(drawingModel.drawings, id: \.uuidString) {
DrawingRow(drawingData: $0)
}
}
} detail: {
DrawingView()
}
.navigationSplitViewStyle(.balanced) // <- If I use automatic style, PKCanvasView's size is correct