Reputation: 1366
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 …
… looks like this on the iPhone:
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
PKDrawing.scale(in:)
functionThe 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