Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use PHCachingImageManager

I know that there is already this question on SO, but I don't think that the given answer is satisfying/complete: How can IOS Photos app can show hundreds of photos in one screen?

What I want to achieve

I want something like the image selection in whatsapp (iOS) (see screenshot). When you open the camera, there is also a horizontal slider where you can see all images from your gallery.

whatsapp screenshot

What I tried

Right now I have the following code in my appDelegate:

let options = PHFetchOptions()

options.sortDescriptors = [
    NSSortDescriptor(key: "creationDate", ascending: true)
]
if let results = PHAsset.fetchAssetsWithMediaType(.Image, options: options) {

    results.enumerateObjectsUsingBlock { (object, idx, _) in
        if let asset = object as? PHAsset {
            Variables.assets.append(asset)
        }
    }

    println(Variables.assets.count)

    Variables.imageManager.startCachingImagesForAssets(Variables.assets, targetSize: CGSizeMake(viewWidth, viewWidth), contentMode: .AspectFill, options: self.requestOptions)
}

Later I load the images in a UITableViewController and call the following function on scroll:

func fetchPhotoAtIndex(index : Int) {

    let asset = Variables.assets[Variables.assets.count - 1 - index] as PHAsset

    Variables.imageManager.requestImageForAsset(asset, targetSize: CGSizeMake(self.viewWidth, self.viewWidth), contentMode: .AspectFill, options: self.requestOptions, resultHandler: { (image, _) in

        println("\(asset.localIdentifier) \(asset.creationDate) ")

        [...]

        self.tableView.reloadData()
    })

}

This works well but I have the problem that on every app start all my assets get cached. Is this the right approach for my problem? How can I display the gallery photos in realtime like whatsapp does?

like image 756
Alexander Scholz Avatar asked May 29 '15 15:05

Alexander Scholz


1 Answers

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.

like image 129
jarora Avatar answered Nov 09 '22 08:11

jarora