UIImageView not showing transparency of PNG Images from UIImagePickerController - ios

I surely hope I am missing something because I do not understand why this is working the way it does. I have a PNG Image, which has a fully transparent background because I want to overlay it on other images inside a UIImageView.
PNG images included in the XCode project all work fine as they should. The problem is when I select these same PNG images on the fly using UIImagePickerController and then assigning it to the UIImageView, for some really bizarre reason, it is not respecting it as a PNG Image with transparency and instead it adding a white background.
Anyone seen this before and how do I get around this?
* UPDATE #1: I decided to try something that seems to confirm my theory. I decided to email myself the original PNG images I saved to my device and lo and behold, the images came to me as JPG. Seems to me that when you save an image to Photos on iPhone it converts it to JPG, this is rather shocking to me. Hope someone has a way around this. The original images testImage1.png and testImage2.png saved to Photos and then emailed back to myself, returned as IMG_XXXX.jpg and IMG_XXXX.jpg
* UPDATE #2: I kept playing around we this more and found out a few things and in the process was able to answer my own question. (1) My theory in UPDATE #1 is partially correct, but the conversion does not happen when saving the Photo, seems like it is on the fly. Internally photos stores the original image extension (2) I was able to validate this when I realized in my UIImagePickerControllerDelegate that I was using
let imageData = UIImageJPEGRepresentation(image, 1.0)
instead of this
let imageData = UIImagePNGRepresentation(image)
When I used the second line of code, it was recognizing the original transparency properties for the image.

Yes, the call to UIImageJPEGRepresentation will convert the resulting image into a JPEG, which doesn't support transparency.
BTW, if your intent is to get the NSData for the image for other reasons (e.g. uploading to server, emailing, etc.), I would recommend against both UIImageJPEGRepresentation and UIImagePNGRepresentation. They lose meta data, can make the asset larger, if suffer some image degradation if you use quality factor of less than 1, etc.
Instead, I'd recommend going back and get the original asset from the Photos framework. Thus, in Swift 3:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let url = info[UIImagePickerControllerReferenceURL] as? URL {
let result = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
if let asset = result.firstObject {
let manager = PHImageManager.default()
manager.requestImageData(for: asset, options: nil) { imageData, dataUTI, orientation, info in
if let fileURL = info!["PHImageFileURLKey"] as? URL {
let filename = fileURL.lastPathComponent
// use filename here
}
// use imageData here
}
}
}
picker.dismiss(animated: true)
}
If you have to support iOS 7, too, you'd use the equivalent ALAssetsLibrary API, but the idea is the same: Get the original asset rather than round-tripping it through a UIImage.
(For Swift 2 rendition, see previous revision of this answer.)

Swift 3 version of answer by #Rob
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let URL = info[UIImagePickerControllerReferenceURL] as? NSURL {
let result = PHAsset.fetchAssets(withALAssetURLs: [URL as URL], options: nil)
if let asset:PHAsset = result.firstObject! as PHAsset {
let manager = PHImageManager.default()
manager.requestImageData(for: asset, options: nil) { imageData, dataUTI, orientation, info in
let fileURL = info!["PHImageFileURLKey"] as? NSURL
let filename = fileURL?.lastPathComponent;
// use imageData here
}
}
}
picker.dismiss(animated: true, completion: nil)
}

An alternative solution uses the PHAssetResourceManager rather than PHImageManager. Using Xcode 10, Swift 4.2.
func imageFromResourceData(phAsset:PHAsset) {
let assetResources = PHAssetResource.assetResources(for: phAsset)
for resource in assetResources {
if resource.type == PHAssetResourceType.photo {
var imageData = Data()
let options = PHAssetResourceRequestOptions()
options.isNetworkAccessAllowed = true
let _ = PHAssetResourceManager.default().requestData(for: resource, options: options, dataReceivedHandler: { (data:Data) in
imageData.append(data)
}, completionHandler: { (error:Error?) in
if error == nil, let picked = UIImage(data: imageData) {
self.handlePickedImage(picked: picked)
}
})
}
}
}
Use it like this:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
dismiss(animated: true)
let mediaType = info[UIImagePickerController.InfoKey.mediaType] as! NSString
if mediaType == kUTTypeImage || mediaType == kUTTypeLivePhoto {
if #available(iOS 11.0, *) {
if let phAsset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset {
self.imageFromResourceData(phAsset: phAsset)
}
else {
if let picked = (info[UIImagePickerController.InfoKey.originalImage] as? UIImage) {
self.handlePickedImage(picked: picked)
}
}
}
else if let picked = (info[UIImagePickerController.InfoKey.originalImage] as? UIImage) {
self.handlePickedImage(picked: picked)
}
}
}

Related

How to detect if an image (a GIF) is animated or still

I need to check if an image is animated or ordinary image. Is there any method in iOS to check this?
I think a file with gif extension can be still image or animated image.
How can I check if it's animated or still image?
For anyone that still comes here looking for an answer in Swift. Just to get if the image is an animated gif, this is the simplest I could come up to.
This is working fine on Swift 4. For Swift 4.2 a couple changes were made to the delegate methods, but nothing major.
THIS ONLY WORKS ON IOS 11+, if you need support for 9/10 see below.
func isAnimatedImage(_ imageUrl: URL) -> Bool {
if let data = try? Data(contentsOf: imageUrl),
let source = CGImageSourceCreateWithData(imageData as CFData, nil) {
let count = CGImageSourceGetCount(source)
return count > 1
}
return false
}
and in my case I'm getting the image from a UIImagePickerController
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
if let imageURL = info[UIImagePickerControllerImageURL] as? URL {
let isImageAnimated = isAnimatedImage(imageURL)
print("isAnimated: \(isImageAnimated)")
}
picker.dismiss(animated: true, completion: nil)
}
IOS 9 and 10
For iOS 9 and 10 theres no way to get the UIImagePickerControllerImageURL since it's a new iOS 11 thing, so we need to use the Photo library to fetch the data from.
Import Photos Library at the top
import Photos
Add the correct code to fetch data
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
if let imageUrl = info[UIImagePickerControllerReferenceURL] as? URL {
let asset = PHAsset.fetchAssets(withALAssetURLs: [imageUrl], options: nil)
if let image = asset.firstObject {
PHImageManager.default().requestImageData(for: image, options: nil) { (imageData, _, _, _) in
let isImageAnimated = isAnimatedImage(imageData)
print("isAnimated: \(isImageAnimated)")
}
}
}
picker.dismiss(animated: true, completion: nil)
}
func isAnimatedImage(_ imageData: Data) -> Bool {
if let source = CGImageSourceCreateWithData(imageData as CFData, nil) {
let count = CGImageSourceGetCount(source)
return count > 1
}
return false
}
the following code snippets maybe help you.
Basically, it uses CGImageSourceGetCount to get the count of images for gif files. Then, it depends on the count to do a image action or the animation action.
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage;
if (count <= 1) {
animatedImage = [[UIImage alloc] initWithData:data];
}
else {
NSMutableArray *images = [NSMutableArray array];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
....
}
}
Those codes from [轉]用UIImage承載GIF圖片
It is really possible but you may create your own method to do that by extending the NSData class.
here is the detail of getting an image extension: Detail

iOS Swift 4: Get Image Format (JPG,PNG,GIF) From didFinishPickingMediaWithInfo

I'm building an app that allows you to upload images from your library to the server. This server is used as essentially an image repository. For this reason, it's absolutely necessary to store it in it's original image format: Either JPG, PNG, or GIF. I.E. If the PNG image has transparency, that HAS to be preserved, it cannot simply be converted to a JPG.
I USED to get the image format using UIImagePickerControllerReferenceURL:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let selectedImage = info[UIImagePickerControllerEditedImage] as! UIImage
let assetPath = info[UIImagePickerControllerReferenceURL] as! NSURL
if (assetPath.absoluteString?.hasSuffix("JPG"))! {
print("JPG")
}
else if (assetPath.absoluteString?.hasSuffix("PNG"))! {
print("PNG")
}
else if (assetPath.absoluteString?.hasSuffix("GIF"))! {
print("GIF")
}
else {
print("Unknown")
}
self.dismiss(animated: true, completion: nil)
self.showImageFieldModal(selectedImage: selectedImage)
}
But UIImagePickerControllerReferenceURL has been deprecated in iOS11. It suggests using UIImagePickerControllerPHAsset, but that's not a URL. I'm not sure what I'm supposed to do with that as a PHAsset object...
In iOS11 you can use the original image url key UIImagePickerControllerImageURL and use URL resourceValues method to get its typeIdentifierKey:
if #available(iOS 11.0, *) {
if let imageURL = info[UIImagePickerControllerImageURL] as? URL {
print(imageURL.typeIdentifier ?? "unknown UTI") // this will print public.jpeg or another file UTI
}
} else {
// Fallback on earlier versions
}
You can use the typeIdentifier extension from this answer to find out the fileURL type identifier:
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
}

why imageData Will return nil in swift 3?

hi I want to convert images into Data but the problem is that the Data will return nil
I want to upload images with Alamofire so I need to convert images before using Alamofire upload here is my codes
let imageData = UIImageJPEGRepresentation(ViewController.imageUpload[i], 1.0)
Alamofire.upload(imageData! , to: "http://example.com/api/file?api_token=\(api_token)&id=\(postID)").responseJSON { response in
debugPrint(response)
}
Try below code to get imageData and make sure you are passing right image there not nil,
For PNG:-
let data = UIImagePNGRepresentation(ViewController.imageUpload[i]) as NSData?
For JPEG:-
let data = UIImageJPEGRepresentation(ViewController.imageUpload[i], 1.0) as NSData?
And also check which image extension you are using while saving (PNG or JPEG)
To know the image extension, please have a look into below code,
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if (!(picker.sourceType == UIImagePickerControllerSourceType.Camera)) {
let assetPath = info[UIImagePickerControllerReferenceURL] as! NSURL
if assetPath.absoluteString.hasSuffix("JPG") {
}
}
}

How can I create a UIImageGIFRepresentation function?

Using Swift 3 for iOS, I am able to use cinemagraphs (animated gifs) that I download through a UIWebView without any problem. I am also trying to get them to work from the iPhone library. However, there is no way that I have found to get the data representation of a GIF stored in the library.
Someone will say that "there is no such thing as an animated gif in the library", but they would be wrong. Go to a web page in Safari, long-tap on an animated gif, and save it to your library. Then send a text message to yourself and copy in that image from the library. You'll see that the animated gif is intact and works perfectly.
My problem is that I need to copy the image from the library programmatically, and I'm not sure how to do it with no specific function built for GIFs. I would like to create the equivalent of UIImagePNGRepresentation or UIImageJPEGRespresentation, except for GIF images. Alternatively, is there a way to get an image from the library as a UIImage, so I don't have to try to convert it to data?
Here's the code I am using for other image formats:
* Edited to be latest version *
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let origImage = info[UIImagePickerControllerOriginalImage] as! UIImage
let refURL : URL = info[UIImagePickerControllerReferenceURL] as! URL
var resizedImage: UIImage!
var ext: String!
ext = refURL.pathExtension
if ext == "GIF" {
resizedImage = origImage
} else {
resizedImage = resizeImage(origImage: origImage, ext: ext)
}
DispatchQueue.main.async(execute: { () -> Void in
self.photoUpdated(newImage: resizedImage)
})
picker.dismiss(animated: true, completion: nil)
}
For GIFs, I tried the following, using the SwiftGif project, which was not successful:
let url = Bundle.main.url(forResource: refURL.absoluteString, withExtension: "GIF")
let data = try! Data(contentsOf: url!)
let advTimeGif = UIImage.gifWithData(data)
let imageView = UIImageView(image: advTimeGif)
newImage = imageView.image
Anyone have any suggestions as to how I might get this to work? Thanks.
Edit:
I figured out a few more pieces of the puzzle. Specifically, I can get the image name and the local path to it.
let refURL : URL = info[UIImagePickerControllerReferenceURL] as! URL
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as String
let localPath = URL(fileURLWithPath: documentDirectory).appendingPathComponent(imageName)
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
Still can't figure out how to get it from there to Data.

How I could access to the Url of photo saved in the camera roll

I would like to save the URL of the photos have been taken from the camera or exinting in the camera roll and have been selected.
Your question is extremely vague with nothing for us to work with. But in any case, I just created an app requiring that logic so I would just share with you
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
// Clear data if picture is repicked
imageLocalURL = nil
imageData = nil
imageSelected = info[UIImagePickerControllerOriginalImage] as? UIImage
if info[UIImagePickerControllerReferenceURL] != nil {
// This would mean that image is chosen from photo library
referenceURL = info[UIImagePickerControllerReferenceURL] as? NSURL
let assets = PHAsset.fetchAssetsWithALAssetURLs([referenceURL! as NSURL], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInputWithOptions(nil, completionHandler: { (contentEditingInput, info) in
// This would be the URL of your image in photo library
self.imageLocalURL = contentEditingInput?.fullSizeImageURL
} else {
// This means that image is taken from camera
imageData = UIImageJPEGRepresentation(imageSelected!, 1.0)
}
dismissViewControllerAnimated(true, completion: nil)
}

Resources