How to Use Phcachingimagemanager

How to use PHCachingImageManager

Refer this Photos Framework example
In this project you should look into the file AAPLAssetGridViewController.m and how it handles caching based on your scroll position.

 - (void)updateCachedAssets
{
BOOL isViewVisible = [self isViewLoaded] && [[self view] window] != nil;
if (!isViewVisible) { return; }

// The preheat window is twice the height of the visible rect
CGRect preheatRect = self.collectionView.bounds;
preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect));

// If scrolled by a "reasonable" amount...
CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect));
if (delta > CGRectGetHeight(self.collectionView.bounds) / 3.0f) {

// Compute the assets to start caching and to stop caching.
NSMutableArray *addedIndexPaths = [NSMutableArray array];
NSMutableArray *removedIndexPaths = [NSMutableArray array];

[self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect removedHandler:^(CGRect removedRect) {
NSArray *indexPaths = [self.collectionView aapl_indexPathsForElementsInRect:removedRect];
[removedIndexPaths addObjectsFromArray:indexPaths];
} addedHandler:^(CGRect addedRect) {
NSArray *indexPaths = [self.collectionView aapl_indexPathsForElementsInRect:addedRect];
[addedIndexPaths addObjectsFromArray:indexPaths];
}];

NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths];
NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths];

[self.imageManager startCachingImagesForAssets:assetsToStartCaching
targetSize:AssetGridThumbnailSize
contentMode:PHImageContentModeAspectFill
options:nil];
[self.imageManager stopCachingImagesForAssets:assetsToStopCaching
targetSize:AssetGridThumbnailSize
contentMode:PHImageContentModeAspectFill
options:nil];

self.previousPreheatRect = preheatRect;
}
}
//In your case this computeDifference method changes to handle horizontal scrolling
- (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect removedHandler:(void (^)(CGRect removedRect))removedHandler addedHandler:(void (^)(CGRect addedRect))addedHandler
{
if (CGRectIntersectsRect(newRect, oldRect)) {
CGFloat oldMaxY = CGRectGetMaxY(oldRect);
CGFloat oldMinY = CGRectGetMinY(oldRect);
CGFloat newMaxY = CGRectGetMaxY(newRect);
CGFloat newMinY = CGRectGetMinY(newRect);
if (newMaxY > oldMaxY) {
CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY));
addedHandler(rectToAdd);
}
if (oldMinY > newMinY) {
CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY));
addedHandler(rectToAdd);
}
if (newMaxY < oldMaxY) {
CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY));
removedHandler(rectToRemove);
}
if (oldMinY < newMinY) {
CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY));
removedHandler(rectToRemove);
}
} else {
addedHandler(newRect);
removedHandler(oldRect);
}
}



- (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths
{
if (indexPaths.count == 0) { return nil; }

NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
for (NSIndexPath *indexPath in indexPaths) {
PHAsset *asset = self.assetsFetchResults[indexPath.item];
[assets addObject:asset];
}
return assets;
}

The image caching manager heats up with your assets of the desired size and then you can retrieve them from the image caching manager itself.

Hope it helps you.

how to use PHCachingImageManager().stopCachingImages

After a lot of trial and error :

PHImageManager.default().requestImageData(for: asset, options: nil) { (data, str, orientation, info) in
let formAnImage = UIImage(data: data!)
//you will get an original image
}

The above option was not working perfectly for all cases, if user selects an older image, the data is nil , So i have used below solution :

            let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.isSynchronous = true
options.resizeMode = PHImageRequestOptionsResizeMode.exact
let targetSize = CGSize(width:SCREENWIDTH(), height:SCREENHEIGHT())

PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: PHImageContentMode.aspectFit, options: options) { (receivedImage, info) in

if let formAnImage = receivedImage
{
//You will get image here..
}
}

swift 4, how do I load over 3000~ pictures in collectionView?

Here are some tips to improve performance when scrolling through the photo library:

Don't implement your own image cache or use the default image manager, use your own instance of PHCachingImageManager. You can tell it to start caching images surrounding your scroll position to improve performance.

Don't use synchronous fetching of images. Use the asynchronous methods and update the image view in the completion block, though note that the completion block can fire before the fetch method has even returned, and fire multiple times as you get better quality images out of the library.

Cancel fetches as your cells go off screen.

I haven't worked with the framework for a while, but I wrote up some notes about it here which are hopefully still relevant.

PHCachingImageManager.requestImage is always called twice?

Affraid there is not much you can do about requestImage:forAsset being called twice. However, you can change how you are putting images in the array to avoid duplicates. Might I suggest:

self.assetsTurnedIntoImages[indexPath.row] = image

This will cause the image passed into completion the second time around to replace the one that was called the first time, avoiding duplication.

You will want to pre-initialize enough space for your array ahead of time so that indexPath.row does not try to access your array out of bounds.
In ViewDidLoad add something to the effect of:

self.assetsTurnedIntoImages = [UIImage](repeating: UIImage(), count: assets.count)

How to reduce memory use with PhotoKit?

What I have done now is

  1. Reduce the target size of fetched PHAsset objects;
  2. Try to adopt caching. My code is like below:

    CGFloat scale = 1.5; //or an even smaller one
    if (!_manager) {
    _manager = [[PHCachingImageManager alloc] init];
    }
    if (!_options) {
    _options = [[PHImageRequestOptions alloc] init];
    }
    _options.resizeMode = PHImageRequestOptionsResizeModeExact;
    _options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;

    NSRange range = NSMakeRange(0, _fetchedPhotos.count);
    NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
    NSArray *assets = [_fetchedPhotos objectsAtIndexes:set];

    CGSize targetSize = CGSizeMake(_layout.itemSize.width*scale, _layout.itemSize.height*scale);

    [_manager startCachingImagesForAssets:assets targetSize:targetSize contentMode:PHImageContentModeAspectFill options:_options];

Now even I scroll the UICollectionView fast enough, the top memory would be less than 50 MB, and thanks to Caching (I guess it is working based on my code) it doesn't fluctuate that much, memory use is like this:

better memory use

UPDATE
According to another post here, it is recommended not to specify PHImageRequestOptions object. Instead, you could leave it to iOS to decide what is the best for you to present the photos with best quality and least time.



Related Topics



Leave a reply



Submit