UIImageView not showing transparency of PNG Images

2019-01-15 19:10发布

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.

3条回答
够拽才男人
2楼-- · 2019-01-15 19:51

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)
        }
    }
}
查看更多
叼着烟拽天下
3楼-- · 2019-01-15 19:54

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.)

查看更多
来,给爷笑一个
4楼-- · 2019-01-15 19:56

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)

}
查看更多
登录 后发表回答