I want to read specified file (photo from camera roll) asynchronous, but it does not work for me.
Variable tempData gets nil untill I change config requestOptionForPhotos.synchronous to YES, then everything is ok, but I don't want to perform this code synchronous.
Is it possible that I'm blocking access to photo by requesting to the same file in other thread? I'm newbie in objective-c and iOS programming and I don't know how does it works.
NSURL *assetUrl = [[NSURL alloc] initWithString:filepath];
PHFetchResult *collection = [PHAsset fetchAssetsWithALAssetURLs:[NSArray arrayWithObject:assetUrl] options:nil];
PHImageRequestOptions *requestOptionForPhotos = [[PHImageRequestOptions alloc] init];
requestOptionForPhotos.networkAccessAllowed = YES;
requestOptionForPhotos.synchronous = NO;
__block BOOL isFinished = NO;
__block NSData * tempData = nil;
for(PHAsset *asset in collection) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(80, 80)
contentMode:PHImageContentModeAspectFill
options:requestOptionForPhotos
resultHandler:^(UIImage *result, NSDictionary *info) {
tempData = UIImagePNGRepresentation(result);
isFinished = YES;
}];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
NSURL *assetUrl = [[NSURL alloc] initWithString:filepath];
PHFetchResult *collection = [PHAsset fetchAssetsWithALAssetURLs:[NSArray arrayWithObject:assetUrl] options:nil];
PHImageRequestOptions *requestOptionForPhotos = [[PHImageRequestOptions alloc] init];
requestOptionForPhotos.networkAccessAllowed = YES;
requestOptionForPhotos.synchronous = NO;
__block BOOL isFinished = NO;
__block NSData * tempData = nil;
for (PHAsset *asset in collection) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(80, 80)
contentMode:PHImageContentModeAspectFill
options:requestOptionForPhotos
resultHandler:^(UIImage *result, NSDictionary *info) {
tempData = UIImagePNGRepresentation(result);
isFinished = YES;
}];
}
});
try this code to get asynchronusly image and put breakpoint on result handdler to check weather you are getting image or not.
Related
I am trying to achieve a simple task like converting a UIImage to NSData, for AVPlayerItem that is returned to me when I select a video from the PHImageManager. What might be an equivalent of the UIImagePNGRepresentation to convert video in data:
PHVideoRequestOptions *videoRequestOptions = [[PHVideoRequestOptions alloc] init];
videoRequestOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
videoRequestOptions.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:videoRequestOptions resultHandler:^(AVPlayerItem *item, NSDictionary *info)
{
//?
}];
Whereas the UIImage goes like this:
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info)
{
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(result)]; //<==THIS
}
The solution is to get the URL from the AVPlayerItem asset, then create NSData from that URL:
PHAsset *asset = ...
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:options resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
NSURL *fileURL = [(AVURLAsset *)playerItem.asset URL];
NSData *videoData = [NSData dataWithContentsOfURL:fileURL];
NSLog(#"tmpData Size: %lu",tmpData.length);
}];
and another way is to use 'requestAVAssetForVideo:asset' ....
PHFetchOptions *fetchOption = [[PHFetchOptions alloc]init];
if ([fetchOption respondsToSelector:#selector(setFetchLimit:)]) {
[fetchOption setFetchLimit:1];
}
PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:#[#"<Video-localIdentifier>"] options:fetchOption].firstObject;
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc]init];
options.version = PHVideoRequestOptionsVersionOriginal;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset
options:options
resultHandler:
^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
NSData *videoData = [NSData dataWithContentsOfURL:urlAsset.URL];
if (videoData) {
NSLog(#"videoData Size: %lu", videoData.length);
}
}];
PHImageManager *manager = [PHImageManager defaultManager];
for (PHAsset *asset in result) {
[manager requestImageForAsset:asset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
_profileImageView.image = image;
//[images addObject:_profileImageView];
}];
}
its not work for me
First make requestoptions like,
self.requestOptions = [[PHImageRequestOptions alloc] init];
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
// this one is key
self.requestOptions.synchronous = true;
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.
Hope this will help :)
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 have the following code to attempt to get a screenshot of a video file from NSData. I can confirm the NSData is valid and not nil, however both dataString and movieURL are returning nil.
- (UIImage *)imageFromMovie:(NSData *)movieData {
// set up the movie player
NSString *dataString = [[NSString alloc] initWithData:movieData encoding:NSUTF8StringEncoding];
NSURL *movieURL = [NSURL URLWithString:dataString];
// get the thumbnail
AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:movieURL options:nil];
AVAssetImageGenerator *generate1 = [[AVAssetImageGenerator alloc] initWithAsset:asset1];
generate1.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
CMTime time = CMTimeMake(1, 2);
CGImageRef oneRef = [generate1 copyCGImageAtTime:time actualTime:NULL error:&err];
UIImage *one = [[UIImage alloc] initWithCGImage:oneRef];
return(one);
}
EDIT: Here's a look at where/how I'm getting the NSData from the UIImagePicker
if ([mediaType isEqualToString:#"ALAssetTypeVideo"]) {
ALAssetsLibrary *assetLibrary=[[ALAssetsLibrary alloc] init];
[assetLibrary assetForURL:[[info objectAtIndex:x] valueForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
unsigned long DataSize = (unsigned long)[rep size];
Byte *buffer = (Byte*)malloc(DataSize);
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:DataSize error:nil];
//here’s the NSData
NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
} failureBlock:^(NSError *err) {
NSLog(#"Error: %#",[err localizedDescription]);
}];
}
Possible, you have problems with encoding.
NSString instance method -(id)initWithData:data:encoding returns nil if data does not represent valid data for encoding.(https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/#//apple_ref/occ/instm/NSString/initWithData:encoding:)
Try to use correct encoding in -(id)initWithData:data:encoding method.
You are trying to convert the movie data to NSURL, that's why you are getting a nil url.
In your implementation, you can get the thumbnail in the following way:
AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:[[info objectAtIndex:x] valueForKey:UIImagePickerControllerReferenceURL] options:nil];
AVAssetImageGenerator *generate1 = [[AVAssetImageGenerator alloc] initWithAsset:asset1];
generate1.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
CMTime time = CMTimeMake(1, 2);
CGImageRef oneRef = [generate1 copyCGImageAtTime:time actualTime:NULL error:&err];
UIImage *one = [[UIImage alloc] initWithCGImage:oneRef];
Download my sample project before reading this answer from:
https://drive.google.com/open?id=0B_exgT43OZJOWl9HMDJCR0cyTW8
I know it's been a really long time since you posted this question; but, I found it, can answer it, and am reasonably confident that, unless you used the sample code provided by the Apple Developer Connection web site that does what you're asking, you still need answer. I base that solely on this fact: it's hard to figure out.
Nonetheless, I have a basic, working project that addresses your question; however, before looking at it, check out a video I made of it running on my iPhone 6s Plus:
<iframe width="640" height="360" src="https://www.youtube.com/embed/GiF-FFKvy5M?rel=0&controls=0&showinfo=0" frameborder="0" allowfullscreen></iframe>
As you can see, the poster frame for every asset in my iPhone's video collection is displayed in UICollectionViewCell; in the UICollectionViewController (or the UICollectionView / datasource delegate:
void (^renderThumbnail)(NSIndexPath *, CustomCell *) = ^(NSIndexPath *indexPath, CustomCell *cell) {
[[PHImageManager defaultManager] requestAVAssetForVideo:AppDelegate.assetsFetchResults[indexPath.section] options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
cell.asset = [asset copy];
cell.frameTime = [NSValue valueWithCMTime:kCMTimeZero];
}];
};
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PHAsset *phAsset = AppDelegate.assetsFetchResults[indexPath.section];
CustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellReuseIdentifier forIndexPath:indexPath];
cell.representedAssetIdentifier = phAsset.localIdentifier;
CGFloat hue = (CGFloat)indexPath.section / 5;
cell.backgroundColor = [UIColor colorWithHue:hue saturation:1.0f brightness:0.5f alpha:1.0f];
if ([cell.representedAssetIdentifier isEqualToString:phAsset.localIdentifier]) {
NSPurgeableData *data = [self.thumbnailCache objectForKey:phAsset.localIdentifier];
[data beginContentAccess];
UIImage *image = [UIImage imageWithData:data];
if (image != nil) {
cell.contentView.layer.contents = (__bridge id)image.CGImage;
NSLog(#"Cached image found");
} else {
renderThumbnail(indexPath, cell);
}
[data endContentAccess];
[data discardContentIfPossible];
}
// Request an image for the asset from the PHCachingImageManager.
/*[AppDelegate.imageManager requestImageForAsset:phAsset
targetSize:cell.contentView.bounds.size
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
// Set the cell's thumbnail image if it's still showing the same asset.
if ([cell.representedAssetIdentifier isEqualToString:phAsset.localIdentifier]) {
cell.thumbnailImage = result;
}
}];*/
return cell;
}
In the UICollectionViewCell subclass:
#implementation CustomCell
- (void)prepareForReuse {
[super prepareForReuse];
_asset = nil;
_frameTime = nil;
_thumbnailImage = nil;
[self.contentView.layer setContents:nil];
[[self contentView] setContentMode:UIViewContentModeScaleAspectFit];
[[self contentView] setClipsToBounds:YES];
}
- (void)dealloc {
}
- (void)setAsset:(AVAsset *)asset {
_asset = asset;
}
- (void)setFrameTime:(NSValue *)frameTime {
_frameTime = frameTime;
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(concurrentQueue, ^{
AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:_asset];
imageGenerator.appliesPreferredTrackTransform = YES;
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
[imageGenerator generateCGImagesAsynchronouslyForTimes:#[frameTime] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
dispatch_sync(dispatch_get_main_queue(), ^{
self.thumbnailImage = [UIImage imageWithCGImage:image scale:25.0 orientation:UIImageOrientationUp];
});
}];
});
}
- (void)setThumbnailImage:(UIImage *)thumbnailImage {
_thumbnailImage = thumbnailImage;
self.contentView.layer.contents = (__bridge id)_thumbnailImage.CGImage;
}
#end
The NSCache is set up like this:
self.thumbnailCache = [[NSCache alloc] init];
self.thumbnailCache.name = #"Thumbnail Cache";
self.thumbnailCache.delegate = self;
self.thumbnailCache.evictsObjectsWithDiscardedContent = true;
self.thumbnailCache.countLimit = AppDelegate.assetsFetchResults.count;
The PHAssets were acquired this way:
- (PHFetchResult *)assetsFetchResults {
__block PHFetchResult *i = self->_assetsFetchResults;
if (!i) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumVideos options:nil];
self->_assetCollection = smartAlbums.firstObject;
if (![self->_assetCollection isKindOfClass:[PHAssetCollection class]]) self->_assetCollection = nil;
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
i = [PHAsset fetchAssetsInAssetCollection:self->_assetCollection options:allPhotosOptions];
self->_assetsFetchResults = i;
});
}
return i;
}
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");
}];
}];