How can I load an background image for editing with PencilKit that is scaled to fit, zoomable, and panable?

95 views Asked by At

I am very new to Swift and mobile development and am trying to create my first simple application for macOS and iPad use.

I am trying to create an application that initially displays an image and allows the user to write on it. I have an elementary example working, but I am having a problem.

Currently, the background image I load is larger than the viewable screen.

  • I'd like it so that the image is scaled to the viewable screen by default but can then be zoomed with standard pinch gestures and panned with 2-finger gestures.
  • The whole image can be written on top of.

I have searched and tried a few things, but I am obviously still missing something about how everything fits together.

Any help/pointers would be much appreciated.

import SwiftUI
import SwiftData
import PencilKit

struct ContentView: View {
    @Environment(\.undoManager) private var undoManager
    @State private var canvasView = PKCanvasView()

    var body: some View {
        
        VStack{
            HStack(spacing: 5) {
                Spacer(minLength: 1)
                Button("Clear") {
                    canvasView.drawing = PKDrawing()
                }.buttonStyle(.borderedProminent)
                Spacer(minLength: 1)
                Button("Undo") {
                    undoManager?.undo()
                }.buttonStyle(.borderedProminent)
                Spacer(minLength: 1)
                Button("Redo") {
                    undoManager?.redo()
                }.buttonStyle(.borderedProminent)
                Spacer(minLength: 1)
                Button("Remove Object") {
                    self.canvasView.tool = PKEraserTool(.vector);
                }.buttonStyle(.borderedProminent)
                Spacer(minLength: 1)
                Button("Erase") {
                    self.canvasView.tool = PKEraserTool(.bitmap, width: 3);
                }.buttonStyle(.borderedProminent)
                Button("Draw") {
                    let color = PKInkingTool.convertColor(.black, from: .dark, to: .light)
                    self.canvasView.tool = PKInkingTool(.monoline, color: color, width: 3)
                }.buttonStyle(.borderedProminent)
                Spacer(minLength: 1)
            }
            
            GeometryReader { proxy in
                VStack {MyCanvas(canvasView: $canvasView)}
                    .frame(width: proxy.size.width, height: proxy.size.height)
                    .background(.black)
            }
        }
    }

}

struct MyCanvas: UIViewRepresentable {
    @Binding var canvasView: PKCanvasView
    //let picker = PKToolPicker()
    let screenSize: CGRect = UIScreen.main.bounds

    func makeUIView(context: Context) -> PKCanvasView {

        //canvasView.contentSize = CGSize(width: 1500, height: 1000)
        canvasView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
        canvasView.minimumZoomScale = 0.2
        canvasView.maximumZoomScale = 4.0
        canvasView.drawingPolicy = .anyInput
        canvasView.isOpaque = false
        canvasView.backgroundColor = UIColor.clear
        canvasView.frame(forAlignmentRect: screenSize)
        canvasView.becomeFirstResponder()

        let imageView = UIImageView(image: UIImage(named: "tracker"))
        imageView.frame(forAlignmentRect: screenSize)
        imageView.contentMode =  UIView.ContentMode.scaleAspectFit

        let contentView = canvasView.subviews[0]
        contentView.addSubview(imageView)
        contentView.sendSubviewToBack(imageView)

        //picker.addObserver(canvasView)
        //picker.setVisible(true, forFirstResponder: canvasView)

        let color = PKInkingTool.convertColor(.black, from: .dark, to: .light)
        canvasView.tool = PKInkingTool(.monoline, color: color, width: 3)
        return canvasView
    }

    func updateUIView(_ uiView: PKCanvasView, context: Context) {
    }
}

#Preview {
    ContentView()
        .modelContainer(for: Item.self, inMemory: true)
}

struct Landscape_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewInterfaceOrientation(.landscapeLeft)
    }
}

Preview of the current implementation:

iOS application screen

0

There are 0 answers