How to fetch images from photo library within range of two dates in iOS?

痞子三分冷 提交于 2019-12-17 20:29:44

问题


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:

  1. You can handle your memory issues by fetching only the images for visible cells instead of fetching the whole lot of them.
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!