Phasset to Uiimage

PHAsset to UIImage

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
}

How to convert PHAsset to UIImage in Objective-C

For anyone struggling as much as I had on this issue, this is the way to go.

First set the requestOptions as:

self.requestOptions = [[PHImageRequestOptions alloc] init];
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;

// this one is key
self.requestOptions.synchronous = YES;

and if there are multiple assets in an array filled with PHAsset objects, then add this code:

self.assets = [NSMutableArray arrayWithArray:assets];
PHImageManager *manager = [PHImageManager defaultManager];
NSMutableArray *images = [NSMutableArray arrayWithCapacity:[assets count]];

// assets contains PHAsset objects.
__block UIImage *ima;

for (PHAsset *asset in self.assets) {
// Do something with the asset

[manager requestImageForAsset:asset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:self.requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
ima = image;

[images addObject:ima];
}];


}

and now the images array contains all the images in uiimage format.

Extracting UIImage from PHAsset video

Welcome ! , To get the thumbnails from the videos first you need to get the PHAsset and then use the PHCachingImageManager() in order to request a thumbnail image from your video.

I will recommend you to check the 'options' area, so you can get the assets in the most suitable way for you.

follow the next code:

let imageManager = PHCachingImageManager()
var allAssetsinAlbum: PHFetchResult<PHAsset>!
var assetsInAlbum = [UIImage]()

func getThumbnail(){
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "title = %@", "YourAlbumName")// Get the assets
let album = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: options)
///- Note: It 's also a good idea to check if the album is available, not doing it here
let thumbnailSize = CGSize(width: 160, height: 160)// you can customize the size here
allAssetsinAlbum = PHAsset.fetchAssets(in: album.firstObject!, options: nil) as PHFetchResult<PHAsset>
let limit = allAssetsinAlbum.count
if limit != 0 {
for i in 0...limit-1 {
let isVideo = allAssetsinAlbum.object(at: i).mediaType == .video ? true : false /// Check if it is a VIDEO
let options = PHImageRequestOptions()//Options: How the thumbnails are obtained
options.isSynchronous = true
options.isNetworkAccessAllowed = true //get those saved in iCloud
options.deliveryMode = .fastFormat// fastest but the quality is not very good
imageManager.requestImage(for: allAssetsinAlbum.object(at: i) , targetSize: thumbnailSize, contentMode: .default, options: options, resultHandler: { asset, _ in /// save video THUMBNAILS
if isVideo{
assetsInAlbum.append(asset!)
}
})
}
}
}

Then I am guessing that you want to display this in a List or something.

struct ContentView: View {
var body: some View {

getThumbnail()
let columns = [GridItem(.adaptive(minimum: 100, maximum: 100))]


return LazyVGrid(columns: columns, spacing: 20 ) {
ForEach(Array(assetsInAlbum.enumerated()), id:\.offset) { index, element in
ZStack{
Image(uiImage: element)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 80, height: 80)
.clipped()
.cornerRadius(5)
}
}
}
}

Hope you find it useful !~

How can I convert a PHLivePhoto to UIImage?

You need to use PHAsset to fetch the asset, then request image data from PHImageManager.default()

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
dismiss(animated: true)

guard let assetIdentifier = results.first?.assetIdentifier else {
return
}

if let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil).firstObject {
PHImageManager.default().requestImageDataAndOrientation(for: phAsset, options: nil) { [weak self] data, _, _, _ in
if let data = data, let image = UIImage(data: data) {
self?.viewModel.imageBucket.append(image)
}
}
}
}

To get assetIdentifier, you need to create PHPickerConfiguration object using the shared photo library. Creating a configuration without a photo library provides only asset data, and doesn't include asset identifiers.

var configuration = PHPickerConfiguration(photoLibrary: .shared())
// Set the filter type according to the user’s selection. .images is a filter to display images, including Live Photos.
configuration.filter = .images
// Set the mode to avoid transcoding, if possible, if your app supports arbitrary image/video encodings.
configuration.preferredAssetRepresentationMode = .current
// Set the selection limit.
configuration.selectionLimit = 1

let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true)

While converting PHAsset to UIImage losing transparency

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
}

How to compare PHAsset to UIImage

You should not compare images, you should instead compare PHAssets or their only useful part named localIdentifier.

The thing that you are looking for to distinguish between assets is called localIdentifier property of PHAsset.

The Apple Docs define it as:

A unique string that persistently identifies the object. (read-only)

Sorry this answer will be a bit broad because I don't like your approach here.

If I were you, I'd do it like this:

First Create a Custom Class, let's name it PhotoInfo. (You don't really have to do this if you are not interested in keeping a lot of info about your photos. If that's the case, use the PFFetchResults of PHAssets directly if you want to. I will however go with CustomClass).

in PhotoInfo.h:

#import <Foundation/Foundation.h>
@interface PhotoInfo : NSObject

@property NSString *localIdentifier;

@end

Now instead of using an array of images, use this custom class you created which will contain localIdentifier. Like this:

PhotoInfo *photo = [[PhotoInfo alloc] init];
photo.localIdentifier = asset.localIdentifier;

Let's say you want to fetch images from gallery, you would do something like this:

-(PHFetchResult*) getAssetsFromLibrary
{
PHFetchResult *allPhotos;
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]]; //Get images sorted by creation date

allPhotos = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:allPhotosOptions];

return allPhotos;
}

To populate your dataSource, you do something like:

NSMutableArray *yourPhotoDataSource = [[NSMutableArray alloc] init];
PHFetchResult * assets = [self getAssetsFromLibrary];
for(PHAsset *asset in assets)
{
PhotoInfo *photo = [PhotoInfo new];
photo.localIndentifier = asset.localIdentifier;
[yourPhotoDataSource addObject:photo];

}

Now let's say you have to display those images somewhere and you need an actual image for that so you will do something like this to get the image:

-(void) getImageForAsset: (PHAsset *) asset andTargetSize: (CGSize) targetSize andSuccessBlock:(void (^)(UIImage * photoObj))successBlock {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
PHImageRequestOptions *requestOptions;

requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.resizeMode = PHImageRequestOptionsResizeModeFast;
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
requestOptions.synchronous = true;
PHImageManager *manager = [PHImageManager defaultManager];
[manager requestImageForAsset:asset
targetSize:targetSize
contentMode:PHImageContentModeDefault
options:requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
@autoreleasepool {

if(image!=nil){
successBlock(image);
}
}
}];
});

}

Now let's say you are displaying those images in a tableView, in your cellForRowAtIndexPath, call the above mentioned method like this:

  //Show a spinner
// Give a customizable size for image. Why load the memory with full image if you don't need to show it?
[self getImageForAsset:asset andTargetSize:yourDesiredCGSizeOfImage andSuccessBlock:^(UIImage *photoObj) {
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI of cell
//Hide the spinner
cell.thumbNail.image = photoObj;
});
}];

Now you are loading images asynchronously with smooth user Experience and saving memory as well by showing only the images which are needed instead of storing all of them. (You can make the performance even better by introducing caching but that's not the point here).

Finally getting to your question, to remove a certain image you will do only need the localIdentifier because it is unique for every PHAsset or Index.

Let's assume you are deleting some cell at your tableView which is showing that specific image which you now want to remove.

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {
PhotoInfo *photo = [yourPhotoDataSource objectAtIndex:indexPath.row];
[yourPhotoDataSource removeObject:photo];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];


}
}

If you are not using a TableView/CollectionView and do not know the index of the object, you can use fast enumeration on your array but then you must know the localIdentifier of the object you wanna delete:

-(void) deletePhotoWithIdentifier:(NSString *) identifierStr{
NSMutableArray *dummyArray = [[NSMutableArray alloc] initWithArray:yourPhotoDataSource]; //created because we can't modify the array we are iterating on. Otherwise it crashes.
[dummyArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(PhotoInfo *p, NSUInteger index, BOOL *stop) {
if ([p.localIndentifier isEqualToString:idenfierStr]) {
[yourPhotoDataSource removeObjectAtIndex:index];
}
}];

}


Related Topics



Leave a reply



Submit