PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = #[[NSSortDescriptor
sortDescriptorWithKey:#"creationDate" ascending:YES]];
_assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
if (_assetsFetchResults.count > 0 ) {
_imageManager = [[PHCachingImageManager alloc] init];
[_arrgalleryImages setObject:[UIImage imageNamed:#"Photo"] atIndexedSubscript:0];
for (int k = 0; k <= 4; k++) {
PHAsset *asset = [_assetsFetchResults objectAtIndex:k];
[_imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsPath = [paths objectAtIndex:0];
NSString* filePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png", k]];
path = filePath;
[imageData writeToFile:path atomically:YES];
[_arrgalleryImages addObject:path];
}];
}
}
Not getting gallery images for the first time, i.e _assetsFetchResults.count gives nil for the first time.
Related
I'am populating gallery assets in my custom app. In my app, apple live images are considered as videos. I explicitly write a video against apple live image. Some of my apple live images are correctly written as videos while i get error for some of my images.
NSError error description says: "the operation could not be completed. [cocoa error -1].
Following method returns error for some media objects:
PHAssetResourceManager's writeDataForAssetResource
following is my method that takes array of apple live identifiers and my custom object array as parameters and do the processing by finding the assets against passed apple live identifiers identifiers in custom objects array.
- (void)updateAssetsToVideoFromLivePhotos:(NSMutableArray*)array objectsArray:(NSMutableArray*) mediaObjectsArray withCompletionHandler:(void (^)(void))completionBlock {
PHFetchOptions *fetchOptions = [PHFetchOptions new];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:array options:fetchOptions];
for (NSInteger i = 0; i < [fetchResult count]; i++) {
PHAsset *phAsset = [fetchResult objectAtIndex:i];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *assetID = phAsset.localIdentifier;
NSRange range = [assetID rangeOfString:#"/"];
if (range.location != NSNotFound) {
assetID = [assetID substringToIndex:range.location];
}
NSString *outputPath = [documentsDirectory stringByAppendingFormat:#"/%#.mp4", assetID];
BOOL videoExists = [[NSFileManager defaultManager] fileExistsAtPath:outputPath];
NSError * errorRef;
if (videoExists) {
[[NSFileManager defaultManager] removeItemAtPath:outputPath error:&errorRef];
}
NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
NSArray *assetResources = [PHAssetResource assetResourcesForAsset:phAsset];
for (NSInteger j = 0; j < [assetResources count]; j++){
PHAssetResource* assetResource = [assetResources objectAtIndex:j];
if (assetResource.type == PHAssetResourceTypePairedVideo){
PHAssetResourceRequestOptions *requestOptions =[PHAssetResourceRequestOptions new];
[[PHAssetResourceManager defaultManager]
writeDataForAssetResource:assetResource
toFile:outputURL
options:requestOptions
completionHandler:^(NSError *error) {
if (!error){
for (NSInteger i = 0; i < [mediaObjectsArray count]; i++){
MyCustomObject *mediaObject = [mediaObjectsArray
objectAtIndex:i];
if ([mediaObject matchesAssetID:assetID]){
[mediaObject updateForAppleLivePhoto:
[outputURL absoluteString]];
break;
}
}
} else {
NSLog(#"%#",[error localizedDescription]);
}
}];
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"mediaUpdated" object:nil];
});
completionBlock();
}
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 GMImagePicker to fetch assets of camera roll.
After selecting single or multiple videos i got PHAsset in arrayArray.
assetArray data in this formate
"<PHAsset: 0x7fe0e8f21130> 40D4733D-4C7B-443D-8093-C28E39ACA45E/L0/001 mediaType=2/0, sourceType=1, (720x480), creationDate=2016-02-14 09:50:58 +0000, location=0, hidden=0, favorite=0 "
How can i store this video into my application document directory?
The code i did to store video is
for (PHAsset *asset in assetArray) {
if (asset.mediaType==PHAssetMediaTypeVideo) {
NSLog(#"Video found");
}
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *URL = [(AVURLAsset *)asset URL];
NSMutableData *responseData=[[NSMutableData alloc] init];
responseData = [NSMutableData dataWithContentsOfURL:URL];
if (responseData.length>0) {
NSString * lastPath = [URL.absoluteString lastPathComponent];//videoname.mp4
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES)objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#",docDirPath,lastPath];
[responseData writeToFile:filePath atomically:YES];
}
}];
}
I also checked filepath where video is saved. Video is available but i get this error.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Please help me to solve this.
According to me use NSMutableData instead of NSData it's accept Nullable value
self.responseData = [NSMutableData dataWithContentsOfURL:location];
if (self.responseData.length>0) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString * lastPath = [url.absoluteString lastPathComponent];//videoname.mp4
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES)objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#",docDirPath,lastPath];
[self.responseData writeToFile:filePath atomically:YES];
}
});
hope this help you.
For storing both Images And Video
for (PHAsset *asset in assetArray) {
if (asset.mediaType==PHAssetMediaTypeVideo) {
NSLog(#"Video found");
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *URL = [(AVURLAsset *)asset URL];
[Albumvideos addObject:URL];
NSData *videoData = [NSData dataWithContentsOfURL:URL];
if (videoData.length>0) {
NSString *lastPath = [URL.absoluteString lastPathComponent];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#-%#",TimeStamp,lastPath]];
[fileManager createFileAtPath:fullPath contents:videoData attributes:nil];
}
}
}];
}
else{
self.requestOptions = [[PHImageRequestOptions alloc] init];
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
// this one is key
self.requestOptions.synchronous = true;
// self.assets = [NSMutableArray arrayWithArray:assets];
PHImageManager *manager = [PHImageManager defaultManager];
// assets contains PHAsset objects.
__block UIImage *ima;
// Do something with the asset
[manager requestImageForAsset:asset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:self.requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
ima = image;
[Albumimages addObject:ima];
}];
}
}
Albumvideos is mutable array which contain video URL and Albumimages contain selected images.
I have a method which retrieves information about a PHAsset, and it works, but when it comes to reading information of hundreds of assets it can be quite slow.
What is the best way to read information about an asset such as filesize and meta-data?
My current code is:
- (void) getAssetInfo: (NSUInteger*) assetIndex {
NSNumber *_assetIndex = [NSNumber numberWithUnsignedInteger: assetIndex];
PHFetchOptions *fetchOptions;
fetchOptions.sortDescriptors = #[ [NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES], ];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithOptions:fetchOptions];
PHAsset *asset = [fetchResult objectAtIndex: assetIndex];
PHImageManager *imageManager = [PHImageManager defaultManager];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = YES;
options.version = PHImageRequestOptionsVersionCurrent;
[asset requestContentEditingInputWithOptions:options
completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
NSString *fileInfo;
NSString *assetURL = nil;
if (contentEditingInput.avAsset != nil){
AVURLAsset *avurlasset_ = (AVURLAsset*) contentEditingInput.avAsset;
assetURL = [avurlasset_.URL absoluteString];
} else {
assetURL = [contentEditingInput.fullSizeImageURL absoluteString];
}
if (assetURL != nil){
uint64_t fileSize;
NSString *filePathFull = [assetURL substringWithRange: NSMakeRange(7, [assetURL length] - 7)];
NSFileManager * filemanager = [[NSFileManager alloc]init];
if([filemanager fileExistsAtPath:filePathFull]){
fileSize = [[filemanager attributesOfItemAtPath:filePathFull error:nil] fileSize];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy,MM,dd,HH,mm,ss"];
NSArray *assembled_FI = [NSArray arrayWithObjects:
(NSNumber*) _assetIndex,
assetURL,
assetURL.lastPathComponent,
(NSNumber*)[NSNumber numberWithUnsignedLongLong: fileSize],
(NSString*) [dateFormatter stringFromDate:asset.creationDate],
nil];
}
}];
}
There is a small library for Swift 3 recently added to GitHub called AssetManager.
It simplifies the access to assets and allows for very fast data extraction.
The link to AssetManager can be found here: https://github.com/aidv/AssetManager
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?