Damiano Miazzi
Damiano Miazzi

Reputation: 2315

Try to save a clear image of QR code in iPhone photo album

I want to save a generated QRcode in the photo album of the iPhone.

With the code below I manage to save it but the quality of the image I get is blurry.

Any I dea how to save the image like it is on the screen.

Data Manager to generate the QRcode

import Foundation
import CoreImage.CIFilterBuiltins
import SwiftUI
import Combine

class Datamanager: ObservableObject {
    let objectWillChange = PassthroughSubject<Void,Never>()
    
    let context = CIContext()
    let filter = CIFilter.qrCodeGenerator()
    
    func generateQRCode(from string: String) -> UIImage {
        let data = Data(string.utf8)
        filter.setValue(data, forKey: "inputMessage")

        if let outputImage = filter.outputImage {
            if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
                return UIImage(cgImage: cgimg)
            }
        }

        return UIImage(systemName: "xmark.circle") ?? UIImage()
    }
    
    func saveImage(image : UIImage) {
       let imageSaver = ImageSaver()
       imageSaver.writeToPhotoAlbum(image: image)
    }
}



class ImageSaver: NSObject {
    func writeToPhotoAlbum(image: UIImage) {
        UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveError), nil)
    }

    @objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
        print("Save finished!")
    }
}

Main View display the QR code and save it with tap on it


struct Scanner3: View {
    
    var dm: Datamanager
    var body: some View {
        VStack {
            
            Image(uiImage: dm.generateQRCode(from: "www.google.it"))
                .interpolation(.none)
                .resizable()
                .scaledToFit()
                .frame(width: 200, height: 200)
            
        }
        .onTapGesture {
            self.dm.saveImage(image: self.dm.generateQRCode(from: "www.google.it"))
        }
        
        
    }
    
}

the image im saving on album is really blurry

Thanks

Upvotes: 0

Views: 1078

Answers (3)

markhunte
markhunte

Reputation: 6932

There are a couple of good scaling extensions in this thread on reddit

Add the extensions.

//Extension Credit: KINGandKONG

    extension UIView {
        func asImage() -> UIImage {
            let format = UIGraphicsImageRendererFormat()
            format.scale = 1
            return UIGraphicsImageRenderer(size: self.layer.frame.size, format: format).image { context in
                self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)
            }
        }
    }
    
    
    extension View {
        func asImage(size: CGSize) -> UIImage {
            let controller = UIHostingController(rootView: self)
            controller.view.bounds = CGRect(origin: .zero, size: size)
            let image = controller.view.asImage()
            return image
        }
    }

And add the let imageSize: CGSize = CGSize(width: 1000, height: 1000) to the view.

This will get you part way.

You then need to do a couple of extra things to ensure the image saves as a crisp image and not a large blurry image.

As you normally would need to add the modifier .resizable() to the Image But also importantly for a Swift generated QR code You need to add the modifier .interpolation(.none)

From This Hacking With Swift.

However, take a close look at the QR code – do you notice how it’s fuzzy? This is because Core Image is generating a tiny image, and SwiftUI is trying to smooth out the pixels as we scale it up.

Line art like QR codes and bar codes is a great candidate for disabling image interpolation. Try adding this modifier to the image to see what I mean:

.interpolation(.none)


This is how I now convert a UIImage of a QR code to an Image View and add the modifiers then pass it through the extension.

let newImage = (Image(uiImage: uiImage).resizable().interpolation(.none)).asImage(size: imageSize)

I then save the newImage to the Photo Library.

Upvotes: 0

Codo
Codo

Reputation: 78835

The generated QR code uses a single pixel for each dot in the QR code. So the resulting image has a very low resolution, about 25 x 25 pixels for a short message. When the image is scaled up to display it, it uses a scaling algorithm that makes it look blurry.

The solution is to create an image with a higher resolution in the first place by applying an transformation:

let transform = CGAffineTransform(scaleX: 100, scaleY: 100)

The solution is described in more detail here: https://medium.com/@MedvedevTheDev/generating-basic-qr-codes-in-swift-63d7222aa011

Upvotes: 2

Frank Rupprecht
Frank Rupprecht

Reputation: 10383

You can try scaling the image up before rendering it into a CGImage:

let scale: CGFloat = 4.0
let transform = CGAffineTransform(scaleX: scale, y: scale)
let outputImage = filter.outputImage?.samplingNearest().transformed(by: transform)

The QR code generator will produce an image in the minimum size that is required to contain the information. You need to scale it up if you want it bigger.
Note: samplingNearest will make sure that the image won't get blurry during scaling.

Upvotes: 0

Related Questions