一个使用简单的图片浏览器, 实现类似微信和今日头条的图片浏览效果 (如果下面的gif图加载不出来或者加载太慢,请移步简书)
简书地址:https://www.jianshu.com/p/baaab7bd47f3
新版本已经基本完成,下面的文档还没有更新,实际使用参考demo为准
LBPhotoBrowser
对gif图片的加载机制:
LBPhotoBrowser对gif的播放提供了两种方式:
(1)采用系统的 + (nullableUIImage*)animatedImageWithImages:(NSArray *)images duration:(NSTimeInterval)durationNS_AVAILABLE_IOS(5_0);
(2)自定义gif的播放,具体步骤如下:
* 获取当前手机可以利用的内存和当前展示的gif图片每帧图片加载到内存占用的大小,以取得当前内存可以加载gif的最大帧数.
最大加载帧数 = 可利用内存 / 每帧图片的大小.
* 使用CADisplayLink作为定时器,开始展示当前帧的图片
* 获取当前帧的展示时间,展示完毕,切换下一帧图片.当在展示当前帧的图片的时候, 异步线程(自定义NSOperation)去取下一帧的图片,以供当前帧的图片展示
完毕后,直接从缓存的buffer(字典)中读取.
* 当gif图片的帧数大于当前内存适合加载的帧数的时候,buffer(字典)会不断的移除已展示过的图片,来确保加载到内存中的图片数稳定.
如果小于可加载的最大帧数,直接全部加载到内存,节省CPU.
* LBPhotoBrowser为了保证较低的CPU消耗,即使在图片浏览器加载多张gif的时候,也会保证同一时间内,只会对一张gif进行处理,不会同时去解压多张gif图片.
建议使用第二种加载方式 即 lowGifMemory = YES, 通过 LBPhotoBrowserManager 的 lowGifMemory 属性控制
当你加载的gif图片较多,并且gif的帧数也比较多,两种方式的差别会特别明显,方式2的优点也越明显.(不要使用模拟器测试)
LBPhotoBrowser
对网络图片的预加载机制:
LBPhotoBrowser 将网络图片的加载分为两种:
(1)缩略图和大图使用同一个url 不需要提供预加载
(2)缩略图和大图使用不同的url 提供预加载
* 当点击图片,通过LBPhotoBrowser展示大图的过程中,LBPhotoBrowser会自动提前加载当前图片左右两张图片,以方便用户浏览
* 当用户在滑动图片的过程中,LBPhotoBrowser会始终保持优先加载当前展示图片和当前展示图片左右两张的图片,并且停止离当前图片较远图片的加载
* 当用户退出LBPhotoBrowser,停止所有图片的加载
当你使用(1)展示图片的时候,请设置`LBPhotoBrowserManager`的`needPreloading` = `NO`.
注:
缩略图: 当前展示给用户的图片
大图: 点击缩略图后,使用LBPhotoBrowser展示给用户的图片
LBPhotoBrowser 对网络图片的预加载机制的进一步优化: 增加 destroyImageNotNeedShow 属性
问 题:
当用户在不停浏览图片的过程中,我们通过预加载机制默默的替用户加载着图片.
比如:用户浏览了20张图片(一般没这么多哈),这时候LBPhotoBrowser会将这20张图片都保存在内存中(会有一个强请引用).(没具体看过其他的图片浏览器怎么做的,不过我猜多数应该也是这样)
这样其实是有些不合理,用户可能只关心当前浏览的图片,至于之前浏览过图片的这时候不应该存在内存当中.一旦用户又要重新浏览这些图片.我们也可以通过预加载机制读取SDWebImage的缓存,这样来减少内存的消耗.
解决办法:
原有的预加载机制,只会加载当前展示的图片和当前展示图片左右两张的图片.不在当前展示范围的图片不会去加载. 已经加载过了的图片会保存在内存中.
故:将所有不在预加载的范围内的图片(已经加载过的),清除内存缓存(取消强引用).
问题: 使用destroyImageNotNeedShow 发现内存没什么变化.有点尴尬😓
原因: SDWebImage会做内存缓存,当我们不对图片强引用的时候,SDWebImage依然会图片有一个强引用. 所以图片不会销毁.
但是SDWebImage 虽然会对图片做一个引用,但是一旦收到内存警告,SDWebImage就会清除这个引用.
解决办法:
对于展示较少张数的图片,不建议开启destroyImageNotNeedShow(默认NO)这个属性
对于展示图片的张数比较多情况(9张以上),开启destroyImageNotNeedShow = YES,进行优化
LBPhotoBrowser
支持本地图片和网络图片 以及gif的播放,下面四中效果详情可参考demo
NSMutableArray *items = [[NSMutableArray alloc]init];
for (UIImageView *imageView in self.imageViews) {
LBPhotoLocalItem *item = [[LBPhotoLocalItem alloc]initWithImage:imageView.image frame:imageView.frame];
[items addObject:item];
}
[[LBPhotoBrowserManager defaultManager]showImageWithWebItems:items selectedIndex:tag fromImageViewSuperView:self.view];
NSMutableArray *items = [[NSMutableArray alloc]init];
for (int i = 0 ; i < cellModel.urls.count; i++) {
LBURLModel *urlModel = cellModel.urls[i];
UIImageView *imageView = cell.imageViews[i];
LBPhotoWebItem *item = [[LBPhotoWebItem alloc]initWithURLString:urlModel.largeURLString frame:imageView.frame];
item.placeholdImage = imageView.image;
[items addObject:item];
}
[LBPhotoBrowserManager.defaultManager showImageWithWebItems:items selectedIndex:tag fromImageViewSuperView:cell.contentView].lowGifMemory = YES;
NSMutableArray *items = [[NSMutableArray alloc]init];
for (int i = 0 ; i < cellModel.urls.count; i++) {
LBURLModel *urlModel = cellModel.urls[i];
UIImageView *imageView = cell.imageViews[i];
LBPhotoWebItem *item = [[LBPhotoWebItem alloc]initWithURLString:urlModel.largeURLString frame:imageView.frame placeholdImage:imageView.image placeholdSize:imageView.frame.size];
[items addObject:item];
}
[LBPhotoBrowserManager.defaultManager showImageWithWebItems:items selectedIndex:tag fromImageViewSuperView:cell.contentView].lowGifMemory = YES;
NSMutableArray *items = [[NSMutableArray alloc]init];
for (int i = 0 ; i < cellModel.urls.count; i++) {
LBURLModel *urlModel = cellModel.urls[i];
UIImageView *imageView = cell.imageViews[i];
LBPhotoWebItem *item = [[LBPhotoWebItem alloc]initWithURLString:urlModel.largeURLString frame:imageView.frame];
[items addObject:item];
}
[LBPhotoBrowserManager.defaultManager showImageWithWebItems:items selectedIndex:tag fromImageViewSuperView:cell.contentView].lowGifMemory = YES;
[[LBPhotoBrowserManager defaultManager]addPhotoBrowserWillDismissBlock:^{
// do something
}].needPreloading = NO;
对于status bar的处理,相比之前做了较大的优化
采用创建一个新的level 高于status bar 的window 覆盖在之前的window上
效果更佳流畅和自然 (上面展示效果的gif图片 是采用了之前版本的处理方式,如果感觉不流畅,请忽略)
由于 SDWebImage 返回的image只是这个gif图片的第一帧
1 当lowGifMemory = NO 的情况下,可以直接过
// 默认长按控件的回调
- (instancetype)addTitleClickCallbackBlock:(void(^)(UIImage *image,NSIndexPath *indexPath,NSString *title))titleClickCallBackBlock;
add 这个block 返回的image 进行保存
2 当lowGifMemory = YES的情况下,通过下面这个block返回的data进行保存, 这个也适合方式1
[[SDImageCache sharedImageCache] queryCacheOperationForKey:currentUrl.absoluteString done:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
}];
所以你可以这样写,可以更长^_^ ,当然也可以分开写
[[[[LBPhotoBrowserManager.defaultManager addLongPressShowTitles:@[@"保存",@"识别二维码",@"取消"]]addTitleClickCallbackBlock:^(UIImage *image, NSIndexPath *indexPath, NSString *title) {
// do somehting
}]addPhotoBrowserWillDismissBlock:^{
// do somehting
}]addPhotoBrowserDidDismissBlock:^{
// do somehting
}];
当你在真机上运行当前版本的时候,你会发现展示gif的一个问题 => 拖动pop当前界面的时候,imageView上的图片不见了
这个是SDWebImage内部的一个方法导致的.
你可以在demo的style3中(右上角有个测试按钮)中找到原因和解决办法
LBPhotoBrowser 只依赖于SDWebImage,本身实现了gif的解压和播放
采用了RAC + MVVM 的方式 使用了LBPhotoBrowser