UIKitiOS综合相关iOS进阶相关

PhotoKit制作相册或选择器(四):预加载策略

2016-09-27  本文已影响251人  Alan_Sim

前言

上一篇文章里我们已经将图片选择器的基本功能完成了。相册里图片比较多的小伙伴有没有觉得上下滚动起来不是很顺呢(当然,像小编这种屌丝挫男可能不会有这烦恼,相册空空如也,没有自拍就没有噩梦),那怎么来处理这种同时请求巨巨巨巨巨多图片的情况呢,这里Apple API里给我们指了条路(蹩脚英文得派上场了LOOOOOOOOOOL


如果需要同时加载多个资源的图片数据,使用PHCachingImageManager类通过加载你马上想要的图片来“预加载”缓存。打个比方,在一个显示图片资源缩略图的collection view内,你可以在滚动到当前位置前就缓存好图片。

使用这些类来请求有关Photos资源的图片,视频,活图内容。
Photos框架根据你的要求自动下载并生成图片,缓存它们,为了更快的复用。为了大量资源能更快地表现,你也可以批请求预加载图片。

那现在的思路就是在UICollectionView滚动前,先缓存可能显示的图片,也就是当前可显示区域前后将要显示的图片。

开始

定义所需属性

//缓存图片管理器
@property (nonatomic, strong) PHCachingImageManager *imageManager;
//先前预加载区域,用于比较
@property CGRect previousPreheatRect;

更新缓存

  1. 判断界面是否显示
  2. 初始化预加载区域大小,在可显示区域基础上前后各增加个可显示区域
  3. 当滚动范围大于当前显示界面的三分之一时,进行缓存操作(由于此方法会在scrollview的代理方法scrollViewDidScroll:中调用,频繁调用会影响性能,这里的规则可以根据需求进行调控)
  4. 区别新增资源以及移除资源,进行缓存更新
- (void)updateCachedAssets {
    BOOL isViewVisible = [self isViewLoaded] && [[self view] window] != nil;
    if (!isViewVisible) { return; }
    
    // 预加载区域是可显示区域的两倍
    CGRect preheatRect = self.collectionView.bounds;
    preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect));
    
    //     比较是否显示的区域与之前预加载的区域有不同
    CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect));
    if (delta > CGRectGetHeight(self.collectionView.bounds) / 3.0f) {
        
        // 区分资源分别操作
        NSMutableArray *addedIndexPaths = [NSMutableArray array];
        NSMutableArray *removedIndexPaths = [NSMutableArray array];
        
        [self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect removedHandler:^(CGRect removedRect) {
            NSArray *indexPaths = [self indexPathsForElementsInCollectionView:self.collectionView rect:removedRect];
            [removedIndexPaths addObjectsFromArray:indexPaths];
        } addedHandler:^(CGRect addedRect) {
            NSArray *indexPaths = [self indexPathsForElementsInCollectionView:self.collectionView rect: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;
    }
}
- (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;
}
- (NSArray *)indexPathsForElementsInCollectionView:(UICollectionView *)collection rect:(CGRect)rect {
    NSArray *allLayoutAttributes = [collection.collectionViewLayout layoutAttributesForElementsInRect:rect];
    if (allLayoutAttributes.count == 0) { return nil; }
    NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:allLayoutAttributes.count];
    for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes) {
        NSIndexPath *indexPath = layoutAttributes.indexPath;
        [indexPaths addObject:indexPath];
    }
    return indexPaths;
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // 可见区域刷新缓存
    [self updateCachedAssets];
}
#pragma mark -- UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self updateCachedAssets];
}

上一篇我们实现了相册变化的检测,那么如果相册资源变化了,缓存如何操作呢?
这里小编直接将缓存重置,简单粗暴~

- (void)resetCachedAssets {
    [self.imageManager stopCachingImagesForAllAssets];
    self.previousPreheatRect = CGRectZero;
}

是不是觉得已经大功告成了?其实还有个很重要的地方要改,不然就白忙活了!
-collectionView:collectionView cellForItemAtIndexPath:这个代理方法内,我们请求图片使用的[PHImageManager defaultManager]换成self.imageManager。因为我们使用的是此对象来进行一系列缓存操作的。

附上DEMO

结束语

这个简单的选择器功能还不够完善,主要是熟悉Photos框架,如果读者感兴趣的话,可以持续关注小编的ASImagePicker

ASImagePicker持续更新中...
https://github.com/alanshen0118/ASImagePicker

文章中有任何错误希望读者能积极指出,我会及时更正。
如果喜欢,请持续关注,顺便点个喜欢噢👇👇👇帮五菱加加油~@_@

Thanks!!!

上一篇下一篇

猜你喜欢

热点阅读