在iOS10中,蘋果為UICollectionViewCell引入了Pre-Fetching預載入機制用於提升它的效能。主要引入了一個新的資料來源協議UICollectionViewDataSourcePrefetching
,包含兩個方法:
@protocol UICollectionViewDataSourcePrefetching <NSObject>
@required
// 預載入資料
- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);
@optional
// 取消提前載入資料
- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);
@end
複製程式碼
網上搜了一大圈,講述原理的(翻譯文件)的文章很多,有乾貨的Demo很少,於是乎自己摸索了一下,寫了一個簡單的案例,在此記錄並分享一下。
執行環境:Xcode 8.2.1 + iOS 10.2
核心步驟:
1、遵從 UICollectionViewDataSourcePrefetching 協議 2、實現 collectionView:prefetchItemsAtIndexPaths 方法和collectionView:cancelPrefetchItemsAtIndexPaths 方法(可選) 3、將第1步中遵從協議的類設定為 UICollectionView 的 prefetchDataSource 屬性
實現
一、建立UICollectionViewFlowLayout
自己寫一個類繼承自UICollectionViewFlowLayout
@implementation MyCollectionViewFlowLayout
-(void)prepareLayout{
self.minimumLineSpacing = 1;//垂直間距
self.minimumInteritemSpacing = 0;//水平間距
self.sectionInset = UIEdgeInsetsMake(0, 0, 8, 0);//分組間距
}
@end
複製程式碼
二、用XIB定義一個
裡面就一個UIImageView,然後拽線設定一個IBOutlet
@property (weak, nonatomic) IBOutlet UIImageView *imgView;
複製程式碼
三、控制器
註釋很詳細
#import "ViewController.h"
#import "MyCollectionViewFlowLayout.h"
#import "ImgCollectionViewCell.h"
#define ScreenW [UIScreen mainScreen].bounds.size.width
//重用標識
static NSString *cellId = @"imgCell";
//遵守協議
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDataSourcePrefetching>
//下載圖片任務
@property(nonatomic, strong) NSMutableDictionary<NSURL *, dispatch_queue_t> *tasks;
//圖片地址
@property(nonatomic, copy) NSMutableArray<NSURL *> *imgURLArray;
//下載的圖片
@property(nonatomic, copy) NSMutableDictionary<NSURL *, UIImage *> *imgs;
//UICollectionView
@property(nonatomic, weak) UICollectionView *collectionView;
@end
@implementation ViewController
//懶載入imgURLArray
-(NSMutableArray<NSURL *> *)imgURLArray{
if (_imgURLArray == nil) {
_imgURLArray = [NSMutableArray array];
for (int i = 0; i < 30; i++) {
NSURL *imgURL = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494499175005&di=1d8d40ac84f4a71cb26d7bf4a5a845ec&imgtype=0&src=http%3A%2F%2Fimg10.360buyimg.com%2Fyixun_zdm%2Fjfs%2Ft2830%2F11%2F2310606472%2F165925%2F962fa94a%2F575f7664Nfd743845.jpg"];
[_imgURLArray addObject:imgURL];
}
}
return _imgURLArray;
}
//懶載入imgs
-(NSMutableDictionary<NSURL *,UIImage *> *)imgs{
if (_imgs == nil) {
_imgs = [NSMutableDictionary dictionary];
}
return _imgs;
}
//懶載入tasks
-(NSMutableDictionary<NSURL *,dispatch_queue_t> *)tasks{
if (_tasks == nil) {
_tasks = [NSMutableDictionary dictionary];
}
return _tasks;
}
- (void)viewDidLoad {
[super viewDidLoad];
//建立UICollectionView
//建立佈局
UICollectionViewLayout *layout = [[MyCollectionViewFlowLayout alloc]init];
//初始化一個UICollectionView
UICollectionView *collection = [[UICollectionView alloc]initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
//設定背景色
collection.backgroundColor = [UIColor groupTableViewBackgroundColor];
//設定代理
collection.dataSource = self;
collection.delegate = self;
collection.prefetchDataSource = self;
//註冊Cell
UINib *nib = [UINib nibWithNibName:@"ImgCollectionViewCell" bundle:nil];
[collection registerNib:nib forCellWithReuseIdentifier:cellId];
[self.view addSubview:collection];
self.collectionView = collection;
}
-(void)loadImage:(NSIndexPath *)indexPath{
NSURL *currentURL = [self.imgURLArray objectAtIndex:indexPath.row];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) weakSelf = self;
//非同步下載圖片
dispatch_async(queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:currentURL];
UIImage *image = [UIImage imageWithData:imageData];
weakSelf.imgs[currentURL] = image;
//更新UI
dispatch_async(dispatch_get_main_queue(), ^{
ImgCollectionViewCell *cell = (ImgCollectionViewCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath];
cell.imgView.image = image;
});
});
//為了取消任務
self.tasks[currentURL] = queue;
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.imgURLArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
ImgCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
// 獲取URL
NSURL *imgURL = self.imgURLArray[indexPath.row];
//對應的URL的圖片已經存在
if (self.imgs[imgURL]) {
cell.imgView.image = self.imgs[imgURL];
}
//不存在
else{
[self loadImage:indexPath];
}
return cell;
}
#pragma mark <UICollectionViewDelegate>
//定義每個Item 的大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat W = (ScreenW - 1) / 2;
return CGSizeMake(W, 100);
}
#pragma mark <UICollectionViewDataSourcePrefetching>
- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
for (NSIndexPath * indexPath in indexPaths) {
NSURL *currentURL = [self.imgURLArray objectAtIndex:indexPath.row];
//不存在就請求
if (!self.imgs[currentURL]) {
[self loadImage:indexPath];
}
}
}
- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
for (NSIndexPath * indexPath in indexPaths){
NSURL *currentURL = [self.imgURLArray objectAtIndex:indexPath.row];
//當前任務存在
if (self.tasks[currentURL]) {
dispatch_queue_t queue = self.tasks[currentURL];
dispatch_suspend(queue);
self.tasks[currentURL] = nil;
}
}
}
@end
複製程式碼
效果
寫在後面的話
1、這個新特性仍然需要探究
2、遇到的一個坑:細心看的話可以發現我的字典是懶載入的,如果直接在viewDidLoad
中初始化會在 weakSelf.imgs[currentURL] = image;
一行報錯,why?煩請知道的告知。