Save GIF Image in Photo Gallery from UIImageView - swift

I have UIImageView which is displaying GIF image, now I am unable to save GIF image into photo gallery.
I am using below swift code
guard let image = imageView.image else { return }
UIImageWriteToSavedPhotosAlbum(image,
self,
#selector(savedImage),
nil);
When I use above code it only save single image in photo gallery not saving as GIF.
Suggestion required, how to save GIF loaded from URL or available in the UIImageView into Gallery.

First your need to get all images of gif from imageView and the duration of the image, now you can generate the gif and save in document directory. Finally, save the file from URL to Photo Album
import ImageIO
import Photos
import MobileCoreServices
#IBAction func btnSaveGifFilePressed(_ sender: Any) {
if let imgs = self.imgView.image?.images, imgs.count > 0 {
var animationDuration = self.imgView.image?.duration ?? 1.0
animationDuration = animationDuration / Double(imgs.count);
let url = FileManager.default.urlForFile("lion.gif")
FileManager.default.createGIF(with: imgs, url: url, frameDelay: animationDuration)
PHPhotoLibrary.shared().performChanges ({
PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: url)
}) { saved, error in
if saved {
print("Your image was successfully saved")
}
}
}
}
Create GIF using the following code.
func createGIF(with images: [UIImage], url: URL, frameDelay: Double) {
if let destinationGIF = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeGIF, images.count, nil) {
let properties = [
(kCGImagePropertyGIFDictionary as String): [(kCGImagePropertyGIFDelayTime as String): frameDelay]
]
for img in images {
let cgImage = img.cgImage
CGImageDestinationAddImage(destinationGIF, cgImage!, properties as CFDictionary?)
}
CGImageDestinationFinalize(destinationGIF)
}
}
Get a URL in single line:
func urlForFile(_ fileName: String, folderName: String? = nil) -> URL {
var documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
if let folder = folderName {
documentsDirectory = documentsDirectory.appendingPathComponent(folder)
if !self.fileExists(atPath: documentsDirectory.path) {
try? self.createDirectory(atPath: documentsDirectory.path, withIntermediateDirectories: true, attributes: nil)
}
}
let fileURL = documentsDirectory.appendingPathComponent(fileName)
return fileURL;
}

Related

How to save selected image from photo gallery to show any view controller in swift

I'm working on my app and i got stuck at one point.
There is one UIIMAGEVIEW and one UPLOAD button.
So when user click UPLOAD button he get to photo gallery to select image after selecting image I'm showing that image on UIIMAGEVIEW.
So my question is:-
after showing image on UIIMAGEVIEW i want to save that image on app to show that same image on different View Controller.
In this case you can try this code snippet:
func saveImgToDocumentDirectory(image: UIImage ) {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileName = "image.png" // image name for identify particular image
let fileURL = documentsDirectory.appendingPathComponent(fileName)
if let data = UIImageJPEGRepresentation(image, 1.0),!FileManager.default.fileExists(atPath: fileURL.path){
do {
try data.write(to: fileURL)
print("file saved")
} catch {
print("error saving file:", error)
}
}
}
func loadImgFromDocumentDirectory(nameOfImage : String) -> UIImage {
let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
if let dirPath = paths.first{
let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(nameOfImage)
let image = UIImage(contentsOfFile: imageURL.path)
return image!
}
return UIImage.init(named: "default.png")!
}
Image name is a unique identifier for the image, while storing you are required to specify the image name "imageName", so it would be easy to fetch that particular image using the same name.
You can put the image in a global variable. A global variable can be used through all the app. :
var imageSelected:UIImage? // Value will be nil if no image has been set
class ViewController1 {
// Puts your image inside a global variable
image = yourImage
}
class ViewController2 {
// Uses the image that was defined inside the global variable
#IBOutlet weak var imageView: UIView!
imageView.image = imageSelected
}

Modify/Edit PhAsset Image by Cropping

is there any way to modify/edit phasset image by cropping or edited one?
I have array of asset, I wanna crop image of asset by getting image from selected asset and passing it cropping controller and in return want to change that cropped image in selected asset.
There is my code to understand well
func presentCropViewController(with:IndexPath) {
self.allPhotos[with.item].getImage { (img) in
if let image = img{
self.indexPathForCropping = with
let cropViewController = CropViewController(image: image)
cropViewController.delegate = self
cropViewController.view.tintColor = UIColor.themeGreen()
self.present(cropViewController, animated: true, completion: nil)
}
}
}
after passing image from asset I got cropped image with this method
func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
cropViewController.dismiss(animated: true, completion: nil)
// Here i get the cropped image and want to update selected asset with this image
}
I'll appreciate if you mentioned down vote reason too so I prepare my question accordinglly
I figured out the solution may its not an efficient way to do but solved my problem
extension PHAsset {
func updateChanges(with img:UIImage,completion:#escaping(PHAsset?)->()){
PHPhotoLibrary.shared().performChanges({
// create cropped image into phphotolibrary
PHAssetChangeRequest.creationRequestForAsset(from: img)
}) { (success, error) in
if success{
// fetch request to get last created asset
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
fetchOptions.fetchLimit = 1
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
if let asset = fetchResult.firstObject{
// replace your selected asset with new cropped one
completion(asset)
}else{
completion(nil)
}
}else{
completion(nil)
}
}
}
}
simply pass cropped/modified image and get new asset with same cropped/modified image

Uiview to Data (Saving a Gif to firebase)

In my app, I take a photo from my camera, and then the user can add a sticker gif on it.
I want to save the result on Firebase Storage (in a gif format)
So to summarize, I want to convert my UIview into Data, to save it on Firebase ( Storage.storage().reference().child("Gif").putData(data, metadata: nil, completion: { (metadata, error) in })
I have tried that :
let yourViewToData = NSKeyedArchiver.archivedData(withRootObject: self.canvasView)
but the result isn't a gif file
I have tried a func like this but I cannot intercept Data on it and I'm not sure of the result :
let img = self.canvasView.toImage()
animatedGif(from: [img])
func animatedGif(from images: [UIImage]) {
let fileProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]] as CFDictionary
let frameProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [(kCGImagePropertyGIFDelayTime as String): 1.0]] as CFDictionary
let documentsDirectoryURL: URL? = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL: URL? = documentsDirectoryURL?.appendingPathComponent("animated.gif")
if let url = fileURL as CFURL? {
if let destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, images.count, nil) {
CGImageDestinationSetProperties(destination, fileProperties)
for image in images {
if let cgImage = image.cgImage {
CGImageDestinationAddImage(destination, cgImage, frameProperties)
}
}
if !CGImageDestinationFinalize(destination) {
print("Failed to finalize the image destination")
}
print("Url = \(fileURL)")
}
}
}
Any idea how I can save my UIView to a gif file on Firebase Storage?
I am looking to make an alternative solution . Take like 15 screenshot of UImage from my UIView, in order to make a gif later. I have this func :
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return snapshotImageFromMyView!
}
Does someone know how can I make like 15 screenshot with this func? Thanks
Thanks !

Save image in Realm

I'm trying to pick image from device's Photo Library in method:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
userPhoto.image = info[UIImagePickerControllerOriginalImage] as! UIImage?
userPhoto.contentMode = .scaleAspectFill
userPhoto.clipsToBounds = true
dismiss(animated: true, completion: nil)
}
and save this picture in Realm (as NSData):
asset.assetImage = UIImagePNGRepresentation(userPhoto.image!)! as NSData?
...
try! myRealm.write
{
user.assetsList.append(asset)
myRealm.add(user)
}
After build succeeded and trying to pick and save image (in the app) i have app error:
'Binary too big'
What i'm doing wrong?
P.S. Sorry for my English :)
After some actions i have this code. But it's overwrite my image.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
let imageUrl = info[UIImagePickerControllerReferenceURL] as! NSURL
let imageName = imageUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
let image = info[UIImagePickerControllerOriginalImage]as! UIImage
let data = UIImagePNGRepresentation(image)
do
{
try data?.write(to: localPath!, options: Data.WritingOptions.atomic)
}
catch
{
// Catch exception here and act accordingly
}
userPhoto.image = image
userPhoto.contentMode = .scaleAspectFill
userPhoto.clipsToBounds = true
urlCatch = (localPath?.path)!
self.dismiss(animated: true, completion: nil);
}
Don't save the image itself into realm, just save the location of the image into realm as String or NSString and load the image from that saved path. Performance wise it's always better to load images from that physical location and your database doesn't get too big
func loadImageFromPath(_ path: NSString) -> UIImage? {
let image = UIImage(contentsOfFile: path as String)
if image == nil {
return UIImage()
} else{
return image
}
}
or you just save the image name, if it's in your documents directory anyhow
func loadImageFromName(_ imgName: String) -> UIImage? {
guard imgName.characters.count > 0 else {
print("ERROR: No image name")
return UIImage()
}
let imgPath = Utils.getDocumentsDirectory().appendingPathComponent(imgName)
let image = ImageUtils.loadImageFromPath(imgPath as NSString)
return image
}
and here a rough example how to save a captured image to your directory with a unique name:
#IBAction func capture(_ sender: AnyObject) {
let videoConnection = stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (imageDataSampleBuffer, error) -> Void in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
//self.stillImage = UIImage(data: imageData!)
//self.savedImage.image = self.stillImage
let timestampFilename = String(Int(Date().timeIntervalSince1970)) + "someName.png"
let filenamePath = URL(fileReferenceLiteralResourceName: getDocumentsDirectory().appendingPathComponent(timestampFilename))
let imgData = try! imageData?.write(to: filenamePath, options: [])
})
/* helper get Document Directory */
class func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
//print("Path: \(documentsDirectory)")
return documentsDirectory as NSString
}
https://realm.io/docs/objc/latest/#current-limitations
maximum data size is 16 MB . this is limitation of realm
Depending on how your serializing the image data (for example if it's a lossless bitmap), it's quite possible that this data exceed 16MB, which as you've stated is Realm's maximum supported size for binary properties.
When you invoke NSData.length, how large does it say your data is?

How to Handle Image Orientation

I have an issue on capture image from camera. When I capture the image from camera using UIImagePickerController and after Capturing I save the image in my document directory and display it on my custom collectionView. But the problem is when I capture image and save it to document directory and when I load the image in my collectionView it display for the user on wrong orientation how can I handle the image orientation? I tried to save image as JPEG its work good but it support only one orientation. I want to save image as png because it support all orientation.
My code:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
// handle image orientation
//end
// code for save image in document directory from camera roll
let fileManager = NSFileManager.defaultManager()
do {
let document = try fileManager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let getFolders = try fileManager.contentsOfDirectoryAtURL(document, includingPropertiesForKeys: nil, options: .SkipsHiddenFiles)
var maxImageNumber:Int = 0 // for get the max number image in document directory
for folder in getFolders {
if folder.lastPathComponent! == albumName {
let getImagesCheck = try fileManager.contentsOfDirectoryAtURL(folder, includingPropertiesForKeys: nil, options: .SkipsHiddenFiles)
let getImages = try fileManager.contentsOfDirectoryAtURL(folder, includingPropertiesForKeys: nil, options: .SkipsHiddenFiles)
if getImages.count <= 0 {
let imageUrl = folder.URLByAppendingPathComponent("Image \(getImagesCheck.count + 1).png")
if let convertImage = UIImagePNGRepresentation(image) {
convertImage.writeToURL(imageUrl, atomically: true)
}
}else {
// continue save images
let getImages_else = try fileManager.contentsOfDirectoryAtURL(folder, includingPropertiesForKeys: nil, options: .SkipsHiddenFiles)
for img in getImages_else {
let getImageName = img.lastPathComponent!
let arrayOne = getImageName.componentsSeparatedByString(".")
let arrayTwo = arrayOne[0].componentsSeparatedByString(" ")
let getImageNumber = Int(arrayTwo[1])
if getImageNumber! > maxImageNumber {
maxImageNumber = getImageNumber!
}
}
let imageUrl = folder.URLByAppendingPathComponent("Image \(maxImageNumber + 1).png")
if let convertImage = UIImagePNGRepresentation(image) {
convertImage.writeToURL(imageUrl, atomically: true)
}
}
}
}
}catch {
print(error)
}
self.dismissViewControllerAnimated(true, completion: nil)
}