I am using code below, but it doesn't include my sublayer
//Create the UIImage
UIGraphicsBeginImageContext(self.localVideoView!.frame.size)
self.localVideoView!.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//Save it to the camera roll
UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
Use this extension:
extension UIView {
var screenShot: UIImage {
return UIGraphicsImageRenderer(size: bounds.size).image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
}
usage:
guard let image = yourView.screenShot.pngData() else { return }
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
naturally add to your info.plist Privacy - Photo Library Additions Usage Description and Privacy and Photo Library Usage Description
Related
I needed to convert my UI Image that I took with AV Camera Foundation into black and white, so after the user 'approves' the photo, I called this function:
func convertToGrayScale(with originalImage:UIImage, imageStyle:String) -> UIImage {
let currentFilter = CIFilter(name: imageStyle)
currentFilter!.setValue(CIImage(image: originalImage), forKey: kCIInputImageKey)
let output = currentFilter!.outputImage
let context = CIContext(options: nil)
let cgimg = context.createCGImage(output!,from: output!.extent)
let processedImage = UIImage(cgImage: cgimg!)
return processedImage
}
in this block of code:
// Save Image to Camera Roll
#IBAction func saveButton(_ sender: Any) {
let newImage = convertToGrayScale(with: image!, imageStyle: "CIPhotoEffectNoir")
let imageToSave = newImage
UIImageWriteToSavedPhotosAlbum(imageToSave, nil, nil, nil)
uploadPhoto()
// downloadPhoto()
dismiss(animated: true, completion: nil)
}
which is triggered when the user approves the photo.
Now, the problem is that while the image is black and white when it is saved to the camera roll, it is obviously not when it is uploaded to the storage, because the upload function (as seen below) passes the unconverted image into the storage:
// Upload to Firebase Storage
func uploadPhoto() {
let imageName = NSUUID().uuidString
let storageRef = Storage.storage().reference().child(MyKeys.imagesFolder).child("\(imageName)")
if let imageData = image!.jpegData(compressionQuality: 1) {
storageRef.putData(imageData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
}
print(metadata as Any)
})
}
else {
self.present(alertVC, animated: true, completion: nil)
return
}
}
I tried taking out the function and directly pasting the code into the #IBA Action function, but when I tried running it on my phone, I couldn't even save the image to the camera roll or even the storage. How should I modify the functions to save this black and white image to the storage?
I am masking an image using an image_mask, the following code shows that:
func maskOriginalImage() -> UIImage? {
if(self.maskImage != nil && self.originalImage != nil){
let maskReference = self.maskImage?.cgImage!
let maskedReference = self.originalImage?.cgImage!.masking(maskReference!)
self.maskImage = nil
return UIImage(cgImage: maskedReference!)
}
return nil
Now I want to save the masked image to photo library.
func saveImageToLibrary(){
UIImageWriteToSavedPhotosAlbum(self.segmentedimage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
But while saving the maskedImage, original image is saved. The workaround I found is to create a new UIimagecontext and draw the maskedimage in the context and use that image to save.
func getImageFromContext() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.size, false, scale)
draw(in: CGRect(origin: .zero, size: size))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
Why I just can't save the masked image directly? I am relatively new to IOS development so any inputs will be helpful.
I have this extension and it works perfect in app target but crash in share extension when trying to rotate image captured on camera. How to rotate image in share extension? Or maybe it possible to load image from Photo Library already in right orientation.
extension UIImage {
func fixOrientation() -> UIImage {
switch imageOrientation {
case .up:
return self
default:
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(in: CGRect(origin: .zero, size: size)) //Thread 1: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=120 MB, unused=0x0)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
}
}
Crash screenshots:
First of all it is clear that you have a memory crash. According to App Extension Programming Guide:
Memory limits for running app extensions are significantly lower than the memory limits imposed on a foreground app. On both platforms, the system may aggressively terminate extensions because users want to return to their main goal in the host app.
And from error it is clear that you exceed 120 mb. But you might wonder what is took so much memory.
According to Optimizing Images
Written by Jordan Morgan:
iOS essentially derives its memory hit from an image’s dimensions - whereas the actual file size has much less to do with it.
So if we calculate size or 4032 x 3024 photo it will be... 46mb for 4 bit color and 79mb for 8 bit color. Pretty big, but still less that a limit...
Thing is - you have two copies of your image. One is original and second one - rotated.
To solve this issue you need load only rotated image into memory, without original. This can be done with Image I/O Framework:
extension UIImage {
static func imageWithFixedOrientation(at url: URL) -> UIImage? {
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else { return nil }
guard let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? Dictionary<CFString, Any> else { return nil }
guard
let width = imageProperties[kCGImagePropertyPixelWidth] as? CGFloat,
let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat
else { return nil }
let options: [NSString: Any] = [
kCGImageSourceThumbnailMaxPixelSize: max(width, height),
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true
]
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil }
return UIImage(cgImage: cgImage)
}
}
In sample app:
extension ViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let url = info[.imageURL] as? URL else { return }
let image = UIImage.imageWithFixedOrientation(at: url)
}
}
it reduced memory peaks from 180+mb to just 80mb.
You may try to use more optimized UIGraphicsImageRendererFormat instead of using old UIGraphicsBeginImageContextWithOptions, you can find more information in this post. So your code will look something like below:
func fixOrientation() -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
format.prefersExtendedRange = true
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image(actions: { context in
var workSize = size;
workSize.width = floor(workSize.width / scale)
workSize.height = floor(workSize.height / scale)
// if the orientation is already correct
// if image.imageOrientation == .up { draw image }
var transform = CGAffineTransform.identity
//TO-DO - set transform depends on current image orientation
//transform =
let ctx = context.cgContext
ctx.concatenate(transform)
guard let cgImageCopy = cgImage else {
return
}
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx.draw(cgImageCopy, in: CGRect(x: 0.0, y:0.0, width: workSize.height, height: workSize.width))
break;
default:
ctx.draw(cgImageCopy, in: CGRect(origin: .zero, size: workSize))
break;
}
})
return image
}
Did you try using the function you provided within an didFinishPicking delegate method?
func photoLibrary() {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self
myPickerController.sourceType = .photoLibrary
self.present(myPickerController, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let image = info[.originalImage] as? UIImage else {
print("No image found")
return
}
// Flip the image here
self.mainImageView.image = image.fixOrientation()
}
Im making a camera app and I want to add a label to the pictures that are taken like in the app MSQRD and save to the photo album. I got the label to display on to the image but when I go to the photo album it shows an image but without the label. What am I doing wrong with my code. Here is the code that Im using currently:
#IBAction func takePicture(sender: AnyObject) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {(sampleBuffer, error) in
if (sampleBuffer != nil) {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
//edited this part it saves the entire view in the photo album except for the image that was taken and the label.
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0)
self.view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//saves captured picture to camera roll.
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil)
//fade in the image that was taken
UIView.animateWithDuration(0.5, delay: 0.1, options: UIViewAnimationOptions.CurveLinear, animations: {
self.capturedImage.image = image
self.capturedImage.alpha = 1.0
}, completion: nil)
}
}
}
You made the capturedImage from UIGraphicsImageContext, but never used. Change UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) to UIImageWriteToSavedPhotosAlbum(capturedImage, nil, nil, nil).
I know how to save the captured photo to the library but I added some extra code because I wanted the label in my camera view to combine together and save. When I try to save it to the photo library it doesn't save with the label. Here is the code I have:
#IBAction func takePicture(sender: AnyObject) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {(sampleBuffer, error) in
if (sampleBuffer != nil) {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
UIGraphicsBeginImageContextWithOptions(self.previewCamera.bounds.size, self.previewCamera.opaque, 0.0)
self.previewCamera.layer.renderInContext(UIGraphicsGetCurrentContext()!)
UIGraphicsEndImageContext()
self.capturedImage.image = UIGraphicsGetImageFromCurrentImageContext()
//saves captured picture to camera roll.
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
}