I have just added MapKit to my app and have a mapView with an overlay of a route taken, how do I save the mapView with the overlay as an image? - swift

As a beginner in swift and app development, I have searched for a solution but cannot find anything.
My app tracks the route walked or run and I need to save the result of the walk with an image of both the mapView and the route, together with the distance etc.
any guidance would be appreciated.
Chris

This function will return a UIImage of any view you need, including your MapKit view. Just call it before you dismiss your controller.
extension UIView {
func snapshot() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, UIScreen.main.scale)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
}
To call it use
let image = mapView.snapshot()

Related

SwiftUI - Saving Image to Share Sheet causes image to save blurry/low res

I have a bit of code in my app that generates a QR Code and scales it up (code reference I used from this link from Hackng with Swift. Now, I'm using the share sheet to allow the user to save the qr code to their camera roll and, it is working, but saving the image low res, and it saves to the camera roll blurry (and i assume if it is shared via other methods it will also be blurry)
Here is the code of my share sheet function:
struct ActivityView: UIViewControllerRepresentable {
let activityItems: [Any]
let applicationActivities: [UIActivity]?
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
return UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {
}
}
and here's the code in my view struct:
.sheet(isPresented: $showShareSheet) {
ShareSheet(activityItems: [self.qrCodeImage])
}
Is there a trick to remove the interpolation on the image when it saves to the share sheet like the .interpolation(.none) on the image view itself?
Your problem is that the QR code image is actually tiny! Like really tiny:
Printing description of image:
<UIImage:0x60000202cc60 anonymous {23, 23}>
When you share this image, the way it will be displayed is dependant on the program or app that will display it, and is out of control of your app as far as I know.
However,
there is a way that you could potentially make it "pretty" in other apps, and this would be to increase the resolution to a larger amount so that when it's rendered it'll appear to have "sharp" pixels.
How would this be accomplished? I think I have an example buried somewhere in old code, I'll dig into it and see if I can find you an example ;)
Edit
I found the code:
extension UIImage {
func resized(toWidth width: CGFloat) -> UIImage? {
let canvasSize = CGSize(width: round(width), height: CGFloat(ceil(width/size.width * size.height)))
UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
defer { UIGraphicsEndImageContext() }
let context = UIGraphicsGetCurrentContext();
context?.interpolationQuality = .none
// Set the quality level to use when rescaling
draw(in: CGRect(origin: .zero, size: canvasSize))
let r = UIGraphicsGetImageFromCurrentImageContext()
return r
}
}
The trick is to provide a way to scale the image, but the real magic is on line 7:
context?.interpolationQuality = .none
If you exclude this line, you'll get blurry images, which is what the OS does by default because you don't generally want to see the pixel edges in images.
You could use this extension like so:
.sheet(isPresented: $showShareSheet) {
ShareSheet(activityItems: [self.qrCodeImage.resized(toWidth: 512) ?? UIImage()])
}
However, this may be resizing the image way more often than necessary. Optimally you'd resize it in the same function that you generate it.

Unable to draw Image Annotation on iOS 13 and iPhones 11, XR etc by PDFKit

I have been assigned a task in which I was supposed to draw some images and some straight lines in between those images. I did it successfully by using this method,
class PDFImageAnnotation: PDFAnnotation {
var image: UIImage?
convenience init(_ image: UIImage?, bounds: CGRect, properties: [AnyHashable : Any]?) {
self.init(bounds: bounds, forType: PDFAnnotationSubtype.ink, withProperties: properties)
self.image = image
}
override func draw(with box: PDFDisplayBox, in context: CGContext) {
super.draw(with: box, in: context)
// Drawing the image within the annotation's bounds.
guard let cgImage = image?.cgImage else { return }
context.draw(cgImage, in: bounds)
}
}
Now, whenever user taps on PDF page, I just add this image annotation at that location. After release of iOS 13 whenever I am tapping on PDF page, this is not showing image annotation, although I receive the touch delegates and my code is working fine on iOS 12. Any possible solution?
I faced similar issue, tap gestures were not adding on the PDFView. It is might be because of stacking of views, I am not sure. But I solved it by adding another UIView on top of my PDFView and adding tap-gesture on it.

How to render a UIView to a UIImage Faster

I'm trying to render a UIView (with subviews) into a UIImage.
I'm using the new iOS 10+ UIGraphicsImageRenderer as seen below
let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let image = renderer.image { ctx in
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
}
But my question is how can I do this faster? I'm trying to render this UIImage before I push a new UIViewController and it has a super long delay until the UIImage is finished processing. I think this is because the renderer UIGraphicsImageRendererContext is being created for every instantiation.
Is there any way to preload the context in the background before it needs to be used to render the image?
Or am I thinking about optimizing this wrong?
Any help would be appreciated.

How to save a couple of images as one image to photo library in swift?

There are three imageViews in first view controller (two donuts png and one Simpson image). Are there any solutions that I can capture these images as one image and save it to photo library? Thanks.
Try this, passing in the base view that has all the subviews you need:
func createImage(from aView:UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSize(width: aView.frame.width, height: aView.frame.height), true, 0)
aView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}

Saving multi layer CALayer image to Library?

So I got a view with a layer and some sublayer that have compositingFilter set with blend modes to see though the layers. I want to save all sublayer to one image in the photo library. Everything looks good in the views but when I save the CALayer, by converting it to a UIImage with UIGraphicsContext and using UIImageWriteToSavedPhotosAlbum, I only get one layer saved.
Dose anyone know how to convert multiple sublayers in a CALayer to a UIImage?
Had the same problem with you, I added many sublayers to a CALayer of UIImageView by imageView.layer.addSublayer(newLayer).
Solved it by following other's instructions.
Hope my code could give any idea.
#IBAction func saveArtwork(_ sender: Any) {
UIGraphicsBeginImageContext(self.imageView.bounds.size)
// The code below may solve your problem
imageView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let activity = UIActivityViewController(activityItems: [image!], applicationActivities: nil)
present(activity, animated: true, completion: nil)
}