I'm attempting to create a UIImage (like a thumbnail or something) from a PHAsset so that I can pass it into something that takes a UIImage. I've tried adapting solutions I found on SO (since they all just directly pass it into say a tableview or something), but I have no success (likely because I'm not doing it right).
func getAssetThumbnail(asset: PHAsset) -> UIImage {
var retimage = UIImage()
println(retimage)
let manager = PHImageManager.defaultManager()
manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: nil, resultHandler: {(result, info)->Void in
retimage = result
})
println(retimage)
return retimage
}
The printlns are telling me that the manager.request line isn't doing anything right now. How do I get it to give me the asset as a UIImage.
Thanks.
This did what I needed it to do, in case anyone also needs this.
func getAssetThumbnail(asset: PHAsset) -> UIImage {
let manager = PHImageManager.defaultManager()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.synchronous = true
manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: option, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
Edit: Swift 3 update
func getAssetThumbnail(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: asset, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
try this it works for me, hope it helps you too,
func getUIImage(asset: PHAsset) -> UIImage? {
var img: UIImage?
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
return img
}
Simple Solution (Swift 4.2)
Method 1:
extension PHAsset {
var image : UIImage {
var thumbnail = UIImage()
let imageManager = PHCachingImageManager()
imageManager.requestImage(for: self, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: nil, resultHandler: { image, _ in
thumbnail = image!
})
return thumbnail
}
}
let image = asset.image
Use this method if you only need UIImage from PHAsset.
OR
extension PHAsset {
func image(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?) -> UIImage {
var thumbnail = UIImage()
let imageManager = PHCachingImageManager()
imageManager.requestImage(for: self, targetSize: targetSize, contentMode: contentMode, options: options, resultHandler: { image, _ in
thumbnail = image!
})
return thumbnail
}
}
let image = asset.image(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?)
Use this method for your desired UIImage.
OR
extension PHAsset {
func image(completionHandler: #escaping (UIImage) -> ()){
var thumbnail = UIImage()
let imageManager = PHCachingImageManager()
imageManager.requestImage(for: self, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: nil, resultHandler: { img, _ in
thumbnail = img!
})
completionHandler(thumbnail)
}
}
let image = asset.image(completionHandler: {(img) in
print("Finished")
})
Use this method for notify after completion.
Method 2:
extension PHAsset {
var data : (UIImage, [AnyHashable : Any]) {
var img = UIImage(); var information = [AnyHashable : Any](); let imageManager = PHCachingImageManager()
imageManager.requestImage(for: self, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: nil, resultHandler: { image,info in
img = image!
information = info!
})
return (img,information)
}
}
let image_withData : (UIImage, [AnyHashable : Any]) = asset.data
Use this method if you want UIImage And Result Info of PHAsset
OR
extension PHAsset {
func data(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?) -> (UIImage, [AnyHashable : Any]) {
var img = UIImage(); var information = [AnyHashable : Any](); let imageManager = PHCachingImageManager()
imageManager.requestImage(for: self, targetSize: targetSize, contentMode: contentMode, options: options, resultHandler: { image,info in
img = image!
information = info!
})
return (img,information)
}
}
let data = asset?.data(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?)
Use this method for your desired Data.
Swift 5
extension PHAsset {
func getAssetThumbnail() -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: self,
targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight),
contentMode: .aspectFit,
options: option,
resultHandler: {(result, info) -> Void in
thumbnail = result!
})
return thumbnail
}
}
Swift 4.
resizeMode,deliveryMode - These can be set according to user requirement.
isNetworkAccessAllowed - set this to "true" for fetching images from the cloud
imageSize- required image size
func getImageFromAsset(asset:PHAsset,imageSize:CGSize, callback:#escaping (_ result:UIImage) -> Void) -> Void{
let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = PHImageRequestOptionsResizeMode.fast
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
requestOptions.isNetworkAccessAllowed = true
requestOptions.isSynchronous = true
PHImageManager.default().requestImage(for: asset, targetSize: imageSize, contentMode: PHImageContentMode.default, options: requestOptions, resultHandler: { (currentImage, info) in
callback(currentImage!)
})
}
I'd suggest using Apple's PHCachingImageManager (that inherits from PHImageManager):
A PHCachingImageManager object fetches or generates image data for photo or video assets
Also, PHCachingImageManager support a better caching mechanism.
Example of fetching a thumbnail synchronous:
let options = PHImageRequestOptions()
options.deliveryMode = .HighQualityFormat
options.synchronous = true // Set it to false for async callback
let imageManager = PHCachingImageManager()
imageManager.requestImageForAsset(YourPHAssetVar,
targetSize: CGSizeMake(CGFloat(160), CGFloat(160)),
contentMode: .AspectFill,
options: options,
resultHandler: { (resultThumbnail : UIImage?, info : [NSObject : AnyObject]?) in
// Assign your thumbnail which is the *resultThumbnail*
}
In addition, you can use PHCachingImageManager to cache your images for faster UI response:
To use a caching image manager:
Create a PHCachingImageManager instance. (This step replaces using the
shared PHImageManager instance.)
Use PHAsset class methods to fetch the assets you’re interested in.
To prepare images for those assets, call the
startCachingImagesForAssets:targetSize:contentMode:options: method
with the target size, content mode, and options you plan to use when
later requesting images for each individual asset.
When you need an image for an individual asset, call the
requestImageForAsset:targetSize:contentMode:options:resultHandler:
method, and pass the same parameters you used when preparing that
asset.
If the image you request is among those already prepared, the
PHCachingImageManager object immediately returns that image.
Otherwise, Photos prepares the image on demand and caches it for later
use.
In our example:
var phAssetArray : [PHAsset] = []
for i in 0..<assets.count
{
phAssetArray.append(assets[i] as! PHAsset)
}
let options = PHImageRequestOptions()
options.deliveryMode = .Opportunistic
options.synchronous = false
self.imageManager.startCachingImagesForAssets(phAssetArray,
targetSize: CGSizeMake(CGFloat(160), CGFloat(160)),
contentMode: .AspectFill,
options: options)
For Swift 3.0.1:
func getAssetThumbnail(asset: PHAsset, size: CGFloat) -> UIImage {
let retinaScale = UIScreen.main.scale
let retinaSquare = CGSize(width: size * retinaScale, height: size * retinaScale)//(size * retinaScale, size * retinaScale)
let cropSizeLength = min(asset.pixelWidth, asset.pixelHeight)
let square = CGRect(x:0, y: 0,width: CGFloat(cropSizeLength),height: CGFloat(cropSizeLength))
let cropRect = square.applying(CGAffineTransform(scaleX: 1.0/CGFloat(asset.pixelWidth), y: 1.0/CGFloat(asset.pixelHeight)))
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
var thumbnail = UIImage()
options.isSynchronous = true
options.deliveryMode = .highQualityFormat
options.resizeMode = .exact
options.normalizedCropRect = cropRect
manager.requestImage(for: asset, targetSize: retinaSquare, contentMode: .aspectFit, options: options, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
Resource : https://gist.github.com/lvterry/f062cf9ae13bca76b0c6#file-getassetthumbnail-swift
The problem is that requestImageForAsset is a resultHandler and this block of code happens in the future after your functions has already printed and returned the value you was expecting. I did come changes to show you this happening and also suggest some simple solutions.
func getAssetThumbnail(asset: PHAsset) {
var retimage = UIImage()
println(retimage)
let manager = PHImageManager.defaultManager()
manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: nil, resultHandler: {
(result, info)->Void in
retimage = result
println("This happens after")
println(retimage)
callReturnImage(retimage) // <- create this method
})
println("This happens before")
}
Learn more about closures and completion handle and async funcs at Apple documentation
I hope that helps you!
Objective-c version of code based on dcheng answer.
-(UIImage *)getAssetThumbnail:(PHAsset * )asset {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = true;
__block UIImage *image;
[PHCachingImageManager.defaultManager requestImageForAsset:asset targetSize:CGSizeMake(100, 100) contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
image = result;
}];
return image;
}
Swift 5 working function
func getImageForAsset(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager().requestImage(for: asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, options: nil, resultHandler: {(result, info) -> Void in
thumbnail = result!
})
return thumbnail
}
I have a different solution which worked really nicely when I wanted to get the memory down in my collectionView:
First I get the URL from the asset:
func getImageUrlFrom(asset: PHAsset, completion: #escaping ((URL?)->())) {
asset.requestContentEditingInput(with: nil, completionHandler: { (input, info) in
if let input = input {
completion(input.fullSizeImageURL)
}
})
}
Then, instead of requesting an image, I downSample it and make it memory efficient for the size of the image: https://developer.apple.com/videos/play/wwdc2018/219
func downsample(imageAt imageURL: URL?,
to pointSize: CGSize,
scale: CGFloat = UIScreen.main.scale) -> UIImage? {
guard let imageURL = imageURL else { return nil }
// Create an CGImageSource that represent an image
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions) else {
return nil
}
// Calculate the desired dimension
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
// Perform downsampling
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
] as CFDictionary
guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
return nil
}
// Return the downsampled image as UIImage
return UIImage(cgImage: downsampledImage)
}
Since I can't use PHCachingImageManager, I just use NSCache and the localIdentifier of the asset as the reference for caching.
And remember to use DispatchQueue.global(qos: .userInitiated).async { } when you call both methods.
Related
I am using below code to get original image from PHAsset:
PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.resizeMode = PHImageRequestOptionsResizeModeNone;
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
[[PHImageManager defaultManager] requestImageForAsset:phasset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
if (image) {
}
}];
But found that, image size is greater than the original size. I picked 54 MB file but found its size 123.5MB in response.
Using below code to calculate image size:
NSData *imgData = UIImageJPEGRepresentation(image, 1.0);
NSLog(#"img size: %#", [[NSByteCountFormatter new] stringFromByteCount:imgData.length]);
Any idea how to get an original image using [[PHImageManager defaultManager] requestImageForAsset: API.
I am going to share my code in Swift 5.
In my case, the following code works well on iOS 14.5.1.
extension PHAsset {
func getImageData(_ index: Int, completionHandler: #escaping((_ index: Int, _ image: UIImage?)->Void)) {
let options = PHImageRequestOptions()
options.isSynchronous = true
options.isNetworkAccessAllowed = true
PHImageManager.default().requestImage(for: self, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
completionHandler(index, image)
})
}
}
Because I call getImageData in background thread and the return value is not correctly ordered, I use a variable named index for calling order and in turn, pass it to completionHanlder so that I can know from where the return value is.
If you don't need index variable, you can delete it.
I wrote a function to get original image below, it worked for me. Hope it works for you, too.
func getAssetThumbnail(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
option.isNetworkAccessAllowed = true
option.resizeMode = .none
manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option) { (result, info) in
thumbnail = result!
}
return thumbnail
}
Use following code to get original image. I have code in Swift, you should convert it to Objective-C
PHImageManager.default().requestImage(for: phAssets.firstObject!, targetSize: PHImageManagerMaximumSize, contentMode: PHImageContentMode.default, options: requestOption) { (image, dictResult) in
// Get file path url of original image.
if let url = dictResult!["PHImageFileURLKey"] as? URL {
self.getImageFromFileUrl(url)
}
}
func getImageFromFileUrl(_ fileUrl: URL) {
// Get dat from url, here I have not using catch block, you must use it for swift.
let imageData = try? Data(contentsOf: fileUrl)
// Get image from data
let image = UIImage(data: imageData!)
// Size count, which is same as original image
let size = ByteCountFormatter()
print("image size = \(size.string(fromByteCount: Int64(imageData!.count)))")
}
For details, I have added comment above the line of code.
I hope this will help you.
guys.
I am getting images URL from photos by using below code.
func getAllImagesURL() -> [URL]
{
var arr_URL = [URL]()
for index in 0..<fetchResult.count
{
imgManager.requestImageData(for: fetchResult.object(at: index) as PHAsset, options: requestOptions, resultHandler: { (imagedata, dataUTI, orientation, info) in
if let fileName = (info?["PHImageFileURLKey"] as? URL)
{
//do sth with file name
arr_URL.append(fileName)
}
})
}
return arr_URL
}
By using this URL key I want to get the image from photos.I have searched and found below code.But it still not working.
func getImage(assetUrl: URL) -> UIImage? {
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
guard let result = asset.firstObject else {
return nil
}
var assetImage: UIImage?
let options = PHImageRequestOptions()
options.isSynchronous = true
PHImageManager.default().requestImage(for: result, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: options) { image, info in
assetImage = image
}
return assetImage
}
It returns nil.So please help me.How to get the image by using URL key.
Thanks in Advance..
In the getImage(assetUrl: URL) -> UIImage? method, you are using
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
here assetUrl is the url that should be taken from if you are using the AssetsLibrary. This library is deprecated from iOS 9.0 onwards. We have to use Photos library instead.
BTW, you are already getting all the images(data) in getAllImageURLs() method. Just convert that method to get all the images and process those images as required. You can use the below method to get all the images.
func getAllImages() -> [UIImage]?
{
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
var allImages = [UIImage]()
for index in 0..<fetchResult.count
{
let asset = fetchResult.object(at: index) as PHAsset
imgManager.requestImage(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: .aspectFill, options: requestOptions, resultHandler: { (uiimage, info) in
allImages.append(uiimage!)
})
}
return allImages
}
NOTE: tweak this method as per your requirements.
After I pick an image from Image picker and try to convert PHAsset to UIImage image is losing transparency of the png Image.
I tried searching everywhere but didn't find anything about it.
func getAssetThumbnail(asset: PHAsset) -> UIImage {
let manager = PHImageManager.defaultManager()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.synchronous = true
manager.requestImageForAsset(asset, targetSize: CGSize(width: 341.0, height: 182.0), contentMode: .AspectFit, options: option, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
Image before
After selecting and setting it to image view
Get the original image data by calling requestImageDataForAsset with PHImageRequestOptions.version = . Original. You can then create the image from UIImage(data: data).
Example:
func getThumbnail(asset: PHAsset) -> UIImage? {
var thumbnail: UIImage?
let manager = PHImageManager.defaultManager()
let options = PHImageRequestOptions()
options.version = .Original
options.synchronous = true
manager.requestImageDataForAsset(asset, options: options) { data, _, _, _ in
if let data = data {
thumbnail = UIImage(data: data)
}
}
return thumbnail
}
This question already has answers here:
How to get Original Image and media type from PHAsset?
(3 answers)
Closed 5 years ago.
I have this function to fetch images from assets:
func fetchImage(asset: PhotoAsset, size: CGSize, resizeMode: ResizeMode = .Exact, completion: (result: UIImage?) -> Void) {
let photoAsset = asset as! PHAsset
var options: PHImageRequestOptions?
switch resizeMode {
case .Exact:
options = PHImageRequestOptions()
options?.resizeMode = .Exact
case .Default:
options = nil
}
manager.requestImageForAsset(
photoAsset,
targetSize: size,
contentMode: .AspectFill,
options: options
) { [weak self] result, _ in
completion(result: result)
}
}
but how can I denfine to fetch the original size image?
Thank you!
Use PHImageManagerMaximumSize for targetSize:
func fetchImage(asset: PhotoAsset, size: CGSize, resizeMode: ResizeMode = .Exact, completion: (result: UIImage?) -> Void) {
let photoAsset = asset as! PHAsset
var options: PHImageRequestOptions?
switch resizeMode {
case .Exact:
options = PHImageRequestOptions()
options?.resizeMode = .Exact
case .Default:
options = nil
}
manager.requestImageForAsset(
photoAsset,
targetSize: PHImageManagerMaximumSize,
contentMode: .AspectFill,
options: options
) { [weak self] result, _ in
completion(result: result)
}
}
I want to put the videos stored on my iPhone to my Google Drive.
I have already done with images, but with videos, it's an other problem...
For images, no problem, I convert my asset to an NSData with this method :
data = UIImagePNGRepresentation(result!)!
And I put the image to my drive !
But, for videos, I tried many different ways, but no, I can't.
How can I do ?
Thanks a lot !
I did it !
This is the solution :
PHCachingImageManager().requestAVAssetForVideo(asset, options: nil, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) in
dispatch_async(dispatch_get_main_queue(), {
let asset = asset as? AVURLAsset
var data = NSData(contentsOfURL: asset.URL)
})
})
And after, you have the good NSData variable which you can use to put your video to the Cloud !
Please add Bellow solution its work for me
if you miss option.isNetworkAccessAllowed = true then you get error for genera the url
private let options: PHVideoRequestOptions = PHVideoRequestOptions()
option.isNetworkAccessAllowed = true
PHImageManager.default().requestAVAsset(-------
Updated for Swift 5
PHImageManager or PHCachingImageManager can be used here
PHImageManager.default().requestAVAsset(forVideo: asset,
options: nil) { (asset, audioMix, info) in
if
let asset = asset as? AVURLAsset,
let data = NSData(contentsOf: asset.url) {
//do smth with data
}
}
}
Fetch synchronously Image/Video Swift 5 + caching
extension PHAsset {
func getImage() -> UIImage? {
let manager = PHCachingImageManager.default
let option = PHImageRequestOptions()
option.isSynchronous = true
var img: UIImage? = nil
manager().requestImage(for: self, targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight), contentMode: .aspectFit, options: nil, resultHandler: {(result, info) -> Void in
img = result!
})
return img
}
func getVideo() -> NSData? {
let manager = PHCachingImageManager.default
let option = PHImageRequestOptions()
option.isSynchronous = true
var resultData: NSData? = nil
manager().requestAVAsset(forVideo: self, options: nil) { (asset, audioMix, info) in
if let asset = asset as? AVURLAsset, let data = NSData(contentsOf: asset.url) {
resultData = data
}
}
return resultData
}
}