iOS开发之UICollectionViewDataSource

作者: YungFan | 来源:发表于2017-05-11 16:05 被阅读977次

    在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


    UICollectionViewCell.png
    @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
    
    

    效果

    效果演示.gif

    写在后面的话

    1、这个新特性仍然需要探究
    2、遇到的一个坑:细心看的话可以发现我的字典是懒加载的,如果直接在viewDidLoad中初始化会在 weakSelf.imgs[currentURL] = image; 一行报错,why?烦请知道的告知。

    源代码

    相关文章

      网友评论

      • 我不讲道理的: 把字典的属性修饰符改成strong:grin:
      • LV大树:学到不少。
        YungFan:@LV大树 :clap:
      • 还要取昵称://更新UI
        dispatch_async(dispatch_get_main_queue(), ^{

        ImgCollectionViewCell *cell = (ImgCollectionViewCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath];
        dispatch_async(dispatch_get_main_queue(), ^{
        cell.imgView.image = image;
        });

        });
        });

        这里为什么两次dispatch_get_main_queue()?
        YungFan:@625fc20b9df9 :smile:都是去主线程执行任务,重复了也没报错,多谢指正,已经更正。
        还要取昵称:@YungFan 哦 我还以为有什么高深的用意:joy:
        YungFan:我没注意,删掉里面的就行
      • 天空中的球:另外也可以注意 下 这个 `prefetchingEnabled ` 属性
        天空中的球:@YungFan 恩,对的
        YungFan:@天空中的球 这个属性在10下面是默认开启的啊 不想开启可以设置为NO
      • e43e5251876a:亲测在viewDidLoad中初始化self.tasks = [NSMutableDictionary dictionary]; 并没有什么问题
        YungFan:@Only_Here 对啊 我也很纳闷 所以才改了一下 匪夷所思
        e43e5251876a:那就再尝试一下吧,不过从实际经验考虑一下, 也不会出现这样的问题哈
        YungFan:@Only_Here :scream:那是什么鬼,难道是RP问题,我也无法理解。
      • bec1bc0ef078:可以:clap:
        YungFan:@一草一枯荣 好好学习 天天向上

      本文标题:iOS开发之UICollectionViewDataSource

      本文链接:https://www.haomeiwen.com/subject/icgntxtx.html