PencilKit save background image and drawing to Album Xcode

1.2k views Asked by At

I want to save my canvas to album, but my subview is not visible when I go back to album. I only have my canvas with my drawings.

What should I change ?

  struct Home : View {
        @State var canvas = PKCanvasView()
        @State var showingAlert = false
        var body: some View{
            
            NavigationView{
                MyCanvas(canvasView: canvas)
                    .navigationBarTitleDisplayMode(.inline)
                    .navigationBarItems(
                        leading:
                            HStack{
                                Button(action: { SaveImage()  }, label: { Image(systemName: "square.and.arrow.down.fill").padding() })
                            })
            }
        }
    
        

My function to save image to album :

        func SaveImage(){
            
            // getting image from canvas....
            
            let image = canvas.drawing.image(from: canvas.drawing.bounds, scale: 1)
            
            // saving to albums....
            
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
            showingAlert = true
        }
    }
    
    

My Canvas, where I add my background image :

    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.isOpaque = false
            self.canvasView.backgroundColor = UIColor.clear
            self.canvasView.becomeFirstResponder()
    
           let imageView = UIImageView(image: UIImage(named: "badmintoncourt"))
            let subView = self.canvasView.subviews[0]
                subView.addSubview(imageView)
                subView.sendSubviewToBack(imageView)
            return canvasView
        }
        
        func updateUIView(_ uiView: PKCanvasView, context: Context) {
            picker.addObserver(canvasView)
            picker.setVisible(true, forFirstResponder: uiView)
            DispatchQueue.main.async {
                uiView.becomeFirstResponder()
            }
        }
    }
2

There are 2 answers

0
FrugalResolution On

This is actually not answering the question, but I save my drawing like this to handle dark mode as well:

let drawing = canvasView.drawing
let frameForImage = canvasView.bounds
let darkImage = thumbnail(drawing: drawing, thumbnailRect: frameForImage, traitCollection: UITraitCollection(userInterfaceStyle: .light))

with the thumbnail function:

func thumbnail(drawing: PKDrawing, thumbnailRect: CGRect, traitCollection: UITraitCollection) -> UIImage {
    var image = UIImage()
    traitCollection.performAsCurrent {
        image = drawing.image(from: thumbnailRect, scale: 1.0)
    }
    return image
}
0
thisIsTheFoxe On

I know it's an old question but I had the same problem. Maybe this will help someone someday..

Redering the UIView

Original Answer

Nice, simple, but note that this will capture the entire view, including any subviews (e.g. if you have a ruler on the image)

extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

Using UIGraphicsBeginImageContextWithOptions

Not as nice, but allows for more customisation and options.

extension PKCanvasView {
    func imageWithBackgroundImage(_ bgImage: UIImage?) -> UIImage? {
        var image: UIImage?
        let rect = self.bounds
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let imageRect = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
        if let context = UIGraphicsGetCurrentContext() {
            if let bgImage {
                bgImage.draw(in: imageRect)
            }
            drawing.image(from: drawing.bounds, scale: 0).draw(in: drawing.bounds)

            image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image
        }
        return nil
    }
}