Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
542 views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...