When fetching images using the Photo framework, I am getting all images of the gallery from the PHAsset object. My question is if the images from PHAsset are globally unique or are they device specific. Can I use the same PHAsset image on different iPhones? i don't want to convert the UIImage to NSData and convert it again from NSData to UIImage.
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
//set up fetch options, mediaType is image.
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
options.predicate = [NSPredicate predicateWithFormat:#"mediaType = %d",PHAssetMediaTypeImage];
for (NSInteger i =0; i < smartAlbums.count; i++) {
PHAssetCollection *assetCollection = smartAlbums[i];
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:options];
NSLog(#"sub album title is %#, count is %ld", assetCollection.localizedTitle, (unsigned long)assetsFetchResult.count);
if (assetsFetchResult.count > 0 && ![assetCollection.localizedTitle isEqualToString: #"Recently Deleted"]) {
for (PHAsset *asset in assetsFetchResult) {
PHImageRequestOptions *option = [PHImageRequestOptions new];
option.synchronous = YES;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height-100) contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
[mutArrAsset addObject:asset];
[self.imagesURLArray addObject:result];
}];
NSLog(#"asset are %#", asset);
}}}
A PHAsset represent a pointer to an image in the user's photo library. People have different images on their phone so it doesn't make sense to pass that pointer to a different phone. PHAsset's id is explicted called localIdentifier for this reason.
If you want to transfer an image from one device to a different device you have to transfer it as NSData.
Related
Im using the following code to access filename of the image that I gotta upload. I need both filename alongside the file path and size.
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
[_imageNameArray addObject:[imageRep filename]];
};
[_uploadTbleView reloadData];
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:refURL resultBlock:resultblock failureBlock:nil];
The problem that Im facing is,
Xcode throws a warning message that ALAssetsLibraryAssetForURLResultBlock is deprecated. How can I replace the above code to get the filename ?
I tried using PHAsset but every time when Im selecting the file, it has got the same file name called asset.jpg for all image files.
You can try this:
PHAsset *asset = nil;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
if (fetchResult != nil && fetchResult.count > 0) {
// get last photo from Photos
asset = [fetchResult lastObject];
}
if (asset) {
// get photo info from this asset
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
[[PHImageManager defaultManager]
requestImageDataForAsset:asset
options:imageRequestOptions
resultHandler:^(NSData *imageData, NSString *dataUTI,
UIImageOrientation orientation,
NSDictionary *info)
{
NSLog(#"info = %#", info);
if ([info objectForKey:#"PHImageFileURLKey"]) {
// path looks like this -
// file:///var/mobile/Media/DCIM/###APPLE/IMG_####.JPG
NSURL *path = [info objectForKey:#"PHImageFileURLKey"];
}
}];
}
I wrote simple library to get photos from camera roll. Unfortunately can't read some of them. I can't preview or convert to NSData
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.includeAssetSourceTypes = PHAssetSourceTypeUserLibrary;
PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];
PHImageRequestOptions *requestOptionForPhotos = [[PHImageRequestOptions alloc] init];
requestOptionForPhotos.networkAccessAllowed = YES;
for(PHAsset *asset in allPhotosResult) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(100, 100)
contentMode:PHImageContentModeAspectFill
options:requestOptionForPhotos
resultHandler:^(UIImage *result, NSDictionary *info) {
NSData *data = UIImagePNGRepresentation(result);
NSString *base = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; // for some of photos there is nil
}];
}
I am using PhotoKit to fetch the photos in one of the system albums like this:
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
[fetchResult enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop) {
NSLog(#"ALBUM NAME: %#", collection.localizedTitle);
if ([collection.localizedTitle isEqualToString:#"Camera Roll"]) {
PHFetchResult *photos = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
NSLog(#"PHOTOS: %ld", photos.count);
_photos = nil;
_photos = #[].mutableCopy;
for (PHAsset *asset in photos) {
[self loadImageFromPHAsset:asset];
}
}
}];
Then in my custom helper method: (void)loadImageFromPHAsset:(PHAsset *)asset, I have this:
-(void)loadImageFromPHAsset:(PHAsset *)asset
{
PHImageManager *manager = [PHImageManager defaultManager];
CGSize targetSize = _layout.itemSize;
[manager requestImageForAsset:asset targetSize:targetSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage *result, NSDictionary *info) {
[_photos addObject:result];
}];
[self.collectionView reloadData];
}
So I have an array of images and I present them in my UICollectionViewCell:
UIImage *image = [_photos objectAtIndex:indexPath.row];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.image = image;
But what I got is like this, it is very blur, how can I make it clear?
First of all the targetSize should be multiplied by scale to account for the screen's scale like this,
CGFloat scale = [UIScreen mainScreen].scale;
CGSize targetSize = CGSizeMake(_layout.itemSize.width*scale, _layout.itemSize.height*scale);
Secondly, try these options to get the exact size image,
//Async call returned on main thread, can return multiple times
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.resizeMode = PHImageRequestOptionsResizeModeExact;
Lastly, if you have performance concerns, use PHCachingManager, I have answered the question regarding performance improvements here
Hope this helps you.
Edit:
I did not realise that the images were stored in an array. Try setting the synchronous flag in image options.
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES;
options.resizeMode = PHImageRequestOptionsResizeModeExact;
I need to get information about an asset without loading it into memory which might take a long time if the asset is very large.
As I will be iterating through all assets available in my device, it might take a very long time.
I am using the following code to load an asset in to an NSData object and also retrieve some information about it:
- (NSData*)getAssetInfo: (NSUInteger*) assetIndex {
PHFetchOptions *fetchOptions;
fetchOptions.sortDescriptors = #[ [NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES], ];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithOptions:fetchOptions];
__block NSData *iData = nil;
PHAsset *asset = [fetchResult objectAtIndex: assetIndex];
PHImageManager *imageManager = [PHImageManager defaultManager];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = YES;
options.version = PHImageRequestOptionsVersionCurrent;
#autoreleasepool {
[imageManager requestImageDataForAsset:asset options:options resultHandler:^(NSData *imgData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
//NSLog(#"requestImageDataForAsset returned info(%#)", info);
NSURL *url = [info valueForKey: #"PHImageFileURLKey"];
NSString *urlAsString = [url absoluteString];
gAssetFilename = [urlAsString lastPathComponent];
gAssetCreationDate = asset.creationDate;
iData = imgData;
}];
}
if (iData != nil) {
return iData;
}
else{
NSLog(#"OOPS!");
return nil;
}
}
This works but as I said, it might take a very long time.
Is it possible to get the size of the asset as well other information about the asset without first fully load it into memory?
The Photo Framework for iOS 8 is able to return a URL for images using the PHImageFileURLKey. However I can't seem to read this URL. I only need read access. So far, any place I attempt to use the URL given, it is treated as though the file doesn't exist at all. How can I get data using the URL?
Code that successfully returns PHAssets and a URL:
PHFetchResult* phFetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil]
PHImageManager *manager = [PHImageManager defaultManager];
PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES; // Force sequential work. We have nothing to do until this block returns.
PHAsset* asset = [phFetchResult objectAtIndex:index];
[manager requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage *resultImage, NSDictionary *info)
{
_image = resultImage;
NSURL* fileURL = [info objectForKey:#"PHImageFileURLKey"];
_imageLocation = [fileURL relativePath];
}];
Code showing nothing found:
NSURL* fileURL = [info objectForKey:#"PHImageFileURLKey"];
NSLog(#"File exist?: %d", [[NSFileManager defaultManager] fileExistsAtPath:[fileURL relativePath]]);
My current workaround is to take the UIImage given and save it to the /tmp directory and then use that URL. It's not an ideal solution and I'd really like to know the purpose of the PHImageFileURLKey
I research on PHImageFileURLKey and i think there is no way to find image data by PHImageFileURLKey but you can find your image data by using asset local identifier instead of PHImageFileURLKey.
My code for your reference:
You can use your asset's local identifier and will get the image in response.
PHFetchResult *savedAssets = [PHAsset fetchAssetsWithLocalIdentifiers:#[asset.localIdentifier] options:nil];
[savedAssets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
//this gets called for every asset from its localIdentifier you saved
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
imageRequestOptions.deliveryMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager]requestImageForAsset:asset targetSize:CGSizeMake(50,50) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
NSLog(#"get image from result");
}];
}];