问题
Context
I am try to fetch images within range of two dates from the photo library.
Firstly, I am getting info of the photo library images one by one in the dictionary form and picking every image date by using key and comparing that date with two dates by using if condition.
If that image's date lies between the two dates, I insert that image into array.
I am Saving images in array because I would like to show them in a collection view.
Problem
While it is working on simulator, it is not working on real device because of memory issues.
I think there are bunch of images in the real device photo library that's why am getting memory issues.
How can I solve this problem?
回答1:
As per our conversation in comments in which you agreed to switch to Photos Framework instead of Assets Library, Rather than saving the images to your array, save the PHAsset's local identifier to your array.
Get the Local Identifiers of the images which lie in your date range
To get Images by Date, first create a utility method to create date, for reusability's sake:
-(NSDate*) getDateForDay:(NSInteger) day andMonth:(NSInteger) month andYear:(NSInteger) year{
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:day];
[comps setMonth:month];
[comps setYear:year];
NSDate *date = [[NSCalendar currentCalendar] dateFromComponents:comps];
return date;
}
You can create startDate and endDate from it like this:
NSDate *startDate = [self getDateForDay:11 andMonth:10 andYear:2015];
NSDate *endDate = [self getDateForDay:15 andMonth:8 andYear:2016];
Now you need to get the FetchResults from Photo Library which exist between this range. Use this method for that:
-(PHFetchResult*) getAssetsFromLibraryWithStartDate:(NSDate *)startDate andEndDate:(NSDate*) endDate
{
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"creationDate > %@ AND creationDate < %@",startDate ,endDate];
PHFetchResult *allPhotos = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
return allPhotos;
}
Now you get the PHFetchResults
of all the Photos which exist within this date range. Now to extract your Data Array of local identifiers, you can use this method:
-(NSMutableArray *) getAssetIdentifiersForFetchResults:(PHFetchResult *) result{
NSMutableArray *identifierArray = [[NSMutableArray alloc] init];
for(PHAsset *asset in result){
NSString *identifierString = asset.localIdentifier;
[identifierArray addObject:identifierString];
}
return identifierArray;
}
Add Methods to fetch/utilize the individual assets when you need them
Now, you will need the PHAsset
for the image. You can use the LocalIdentifier like this to get PHAsset
:
-(void) getPHAssetWithIdentifier:(NSString *) localIdentifier andSuccessBlock:(void (^)(id asset))successBlock failure:(void (^)(NSError *))failureBlock{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *identifiers = [[NSArray alloc] initWithObjects:localIdentifier, nil];
PHFetchResult *savedAssets = [PHAsset fetchAssetsWithLocalIdentifiers:identifiers options:nil];
if(savedAssets.count>0)
{
successBlock(savedAssets[0]);
}
else
{
NSError *error;
failureBlock(error);
}
});
}
Then using this PHAsset
, you can get the image for your required size (Try to keep it as minimum as possible to minimize memory usage):
-(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);
}
}
}];
});
}
But don't call these method directly to get all the images you desire.
Instead, call these methods in your cellForItemAtIndexPath
method like:
//Show spinner
[self getPHAssetWithIdentifier:yourLocalIdentifierAtIndexPath andSuccessBlock:^(id assetObj) {
PHAsset *asset = (PHAsset*)assetObj;
[self getImageForAsset:asset andTargetSize:yourTargetCGSize andSuccessBlock:^(UIImage *photoObj) {
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI of cell
//Hide spinner
cell.imgViewBg.image = photoObj;
});
}];
} failure:^(NSError *err) {
//Some error occurred in fetching the image
}];
Conclusion
So in conclusion:
- You can handle your memory issues by fetching only the images for visible cells instead of fetching the whole lot of them.
- You can optimize your performance by fetching the images on background threads.
If you want to get all your assets together anyways, you can get it using fetchAssetCollectionWithLocalIdentifiers: method though I will recommend against it.
Please drop a comment if you have any questions or if you have any other feedback.
Credits to Lyndsey Scott for setting predicate to PHFetchResult request for getting images between two dates in her answer here
回答2:
Why are you saving images in array. if image is in between two dates, just store names of images in array. and then use below code to get and use image through name from library
NSString* photoName = [NSString stringWithFormat:@"%@.png",imageName];
NSArray *arrayPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES);
NSString *path = [arrayPaths objectAtIndex:0];
NSString* imagePath = [path stringByAppendingPathComponent: photoName];
UIImage *image1=[UIImage imageWithContentsOfFile: imagePath];
imageView.image=image1;
来源:https://stackoverflow.com/questions/39587405/how-to-fetch-images-from-photo-library-within-range-of-two-dates-in-ios