Reputation: 2315
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
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
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
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