D14:UICollectionView(单分组, 多分组, 模拟穷游App的折扣页面)

UICollectionView网格视图

网格视图是能够显示多列的列表视图, 弥补了UITableView不方便实现多列的缺点, 在iOS6之后才有这个控件
Inheritance
NSObject
UIResponder
UIView
UIScrollView
UICollectionView

UICollectionViewFlowLayout: 确定网格视图的布局

上下左右的间距: sectionInset(left, top, bottom, right)
每一个Cell的大小: itemSize(width, height)
横向Cell之间的间距: minimumInteritemSpacing
纵向Cell之间的间距: minimumLineSpacing


目录

一.单分组的网格视图

  1. 系统的Cell
  2. 代码自定制的Cell
  3. xib自定义Cell

二.多分组的网格视图

  1. Header
  2. Footer

三.实现穷游App的折扣界面

  1. 创建模型类, 导入MyDownloader类(用于从网上下载数据)
  2. 解析下载到的JSON数据, 创建数据源(此处下载到的是JSON数据, 以后也可能需要下载解析XML数据)
  3. 自定义Cell
  4. 常规流程: 创建CollectionView, 遵守协议实现方法

一.单分组的网格视图

  1. 使用系统的Cell
    1. 创建数据源

    2. 创建UICollectionView对象

      1. 指定初始化方法

         // 第一个参数: 位置    
         // 第二个参数: 布局对象
         - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;  
         UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 20, kWidthOfScreen, kHeightOfScreen - 20) collectionViewLayout:layout];  
        
      2. 布局对象 UICollectionViewLayout

        // 布局对象是一个UICollectionViewLayout类型的对象
        // 由于我们是规则的布局, 可以直接使用UICollectionViewFlowLayout对象
        // UICollectionViewFlowLayout继承于UICollectionViewLayout
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        // 1. 上下左右的间距
        /*
         UIEdgeInsets:<#CGFloat top#>(上面的间距), <#CGFloat left#>(左边), <#CGFloat bottom#>(底部), <#CGFloat right#>(右边)
         */
        layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
        // 2. cell的大小
        layout.itemSize = CGSizeMake(180, 100);
        // 3. 横向间距
        layout.minimumInteritemSpacing = 5;
        // 4. 纵向间距
        layout.minimumLineSpacing = 10;
        
      3. 设置UICollectionView对象的属性

         // 设置代理
         collectionView.delegate = self;
         // 设置数据源代理
         collectionView.dataSource = self;
         // 设置白色背景
         collectionView.backgroundColor = [UIColor whiteColor];
         
         // 注册cell的类型
         // 以代码的方式注册Cell的类型, 表示创建Cell的时候用这个类型来创建
         /*
          第一个参数: Cell的类型
          第二个参数: 重用标志
          */
         [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kCellReuseId];
         
         // 添加到父视图上
         [self.view addSubview:collectionView];  
        
    3. 遵守协议, 实现协议方法
      - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

       // 返回每一个Cell的对象
       // UICollectionView上面的每一个Cell是UICollectionViewCell类型(或子类型)的对象
       // UICollectionViewCell也是一个视图
       - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
       {
           // 从重用队列里面获取
           /*
            第一个参数: 重用id
            第二个参数: cell的位置
            */
           
           // UITableView      -> NSIndexPath:section row
           // UICollectionView -> NSIndexPath:section item
           UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellReuseId forIndexPath:indexPath];
       
           // 不需要再创建, 从dequeueReusableCellWithReuseIdentifier方法李米娜一定能够获取到
           // 设置背景颜色
           cell.backgroundColor = [UIColor grayColor];
           
           // 显示文字
           UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, 180, 40)];
           label.textAlignment = NSTextAlignmentCenter;
           // 获取文字
           NSString *str = self.dataArray[indexPath.item];
           label.text = str;
           // 添加到父视图
           [cell.contentView addSubview:label];
           
           return cell;
       }  
      
    4. 因为每次都是新添一个UILabel, 所以会有滑动出现重影的问题(其实是UILabel的重叠)

       // 解决方法, 在为cell设新的UILabel前移除之前的子视图
          for (UIView *sub in cell.contentView.subviews) {
              [sub removeFromSuperview];
          }
      
  2. 代码自定制的Cell
    1. 创建数据源
    2. 创建UICollectionView对象
      1. 指定初始化方法
        - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
      2. 布局对象 UICollectionViewLayout
      3. 设置UICollectionView对象的属性
    3. 遵守协议, 实现协议方法
    4. 因为自建的cell的控件是其属性, 所以每次重新设置cell的控件的属性不会发生重影的问题
  3. Xib定制的Cell
    1. 创建数据源

    2. 创建UICollectionView对象

      1. 指定初始化方法
        - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;

      2. 布局对象 UICollectionViewLayout (可以使用UICollectionViewDelegateFlowLayout代理来设置)

        Getting the Size of Items
        – collectionView:layout:sizeForItemAtIndexPath:
        Getting the Section Spacing
        – collectionView:layout:insetForSectionAtIndex:
        – collectionView:layout:minimumLineSpacingForSectionAtIndex:
        – collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
        Getting the Header and Footer Sizes
        – collectionView:layout:referenceSizeForHeaderInSection:
        – collectionView:layout:referenceSizeForFooterInSection:
        
      3. 设置UICollectionView对象的属性

      4. 注册cell的类型

         // 注册cell(xib)
         // 第一个参数: xib的对象(UINib类型)
         // 第二个参数: 重用标志
         UINib *nib = [UINib nibWithNibName:@"DataCell" bundle:nil];
         [collectionView registerNib:nib forCellWithReuseIdentifier:kCellReuseId];
        
    3. 遵守协议, 实现协议方法
      - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
      {
      // 从重用列表中获取
      DataCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellReuseId forIndexPath:indexPath];
      ..........................................................................
      }

代码自定制的cell和xib的区别:注册Cell方法的不同
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier


二.多分组的网格视图

  1. Header
    1. 新建headerView类, 继承自UICollectionReusableView

    2. 注册header

       /*
        第一个参数:header视图对象的类型
        第二个参数:区分是header还是后面的footer
        // UICollectionElementKindSectionHeader表示header类型
        第三个参数:重用标志
        */
       [_collectionView registerClass:[HeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:KHeaderReuseId];
      
    3. 在布局对象的代理协议方法中设置header的大小
      - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
      {
      return CGSizeMake(375, 40);
      }

    4. 返回header对象 UICollectionViewDataSource的协议方法(也可以用来返回footer对象)
      - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
      {
      if (kind == UICollectionElementKindSectionHeader) {
      // header类型

               // 从重用队列里面获取
               HeaderView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:KHeaderReuseId forIndexPath:indexPath];
               
               // 设置背景颜色
               header.backgroundColor = [UIColor redColor];
               // 显示数据
               header.titleLabel.text = [NSString stringWithFormat:@"第%c组", (int)(indexPath.section + 'A')];
               return header;
       
           } else if (kind == UICollectionElementKindSectionFooter) {
               // footer
               
               // 从重用队列里面获取
               FooterView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:KFooterReuseId forIndexPath:indexPath];
               
               footer.backgroundColor = [UIColor purpleColor];
               // 显示数据
               footer.titleLabel.text = [NSString stringWithFormat:@"第%c组结束", (int)(indexPath.section + 'A')];
               return footer;
           }
           return nil;
       }  
      
  2. Footer
    1. 新建FooterView类, 继承自UICollectionReusableView

    2. 注册footer

       [_collectionView registerClass:[FooterView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:KFooterReuseId];
      
    3. 在布局对象的代理协议方法中设置footer的大小
      - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
      {
      return CGSizeMake(375, 40);
      }

    4. 返回footer对象 UICollectionViewDataSource的协议方法(也可以用来返回header对象)


三.实现穷游App的折扣界面

  1. 创建模型类, 导入MyDownloader类(用于从网上下载数据)
     // 下载数据
     - (void)downloadData
     {
         // 创建myDownloader对象
         MyDownloader *downloader = [[MyDownloader alloc] init];
         // 设置代理
         downloader.delegate = self;
         // 下载数据
         [downloader downloadWithUrlString:kUrl];
     }
     
     // MyDownloader代理方法
     // 下载失败
     - (void)downloaderFail:(NSError *)error downloader:(MyDownloader *)downloader
     {
         NSLog(@"%@", error);
     }
    
  2. 解析下载到的JSON数据, 创建数据源(此处下载到的是JSON数据, 以后也可能下载XML数据)
     // 下载成功
     - (void)downloaderFinish:(MyDownloader *)downloader
     {
         // 下载回来的数据在downloader.receiveData这个属性里
         // JSON解析
         id result = [NSJSONSerialization JSONObjectWithData:downloader.receiveData options:NSJSONReadingMutableContainers error:nil];
         if ([result isKindOfClass:[NSDictionary class]]) {
             // 如果是字典 按照字典来操作
             NSDictionary *dict = result;
             NSArray *array = dict[@"data"];
             for (NSDictionary *dataDict in array) {
                 // NSLog(@"%@", dataDict);
                 
                 // 创建模型对象
                 DataModel *model = [[DataModel alloc] init];
                 [model setValuesForKeysWithDictionary:dataDict];
                 
                 // 添加到数组中
                 [self.dataArray addObject:model];
             }
             // 刷新网格视图
             [_collectionView reloadData];
         }
     }
    
    • 因为上方使用KVC方法来创建模型对象, 所以我们要在模型类的.m文件中重写方法
      - (void)setValue:(id)value forUndefinedKey:(NSString *)key
      {
      }
  3. 自定义Cell
    • DataCell.m
      - (void)config:(DataModel *)model
      {
      // 图片
      [self.picImageView sd_setImageWithURL:[NSURL URLWithString:model.pic]];

            // 价格
            self.priceLabel.text = model.buy_price;
            
            // 名字
            self.nameLabel.text = model.title;
            self.nameLabel.numberOfLines = 2;
            self.nameLabel.font = [UIFont boldSystemFontOfSize:18];
            
            // 时间
            self.timeLabel.text = model.end_date;
            
            // 折扣
            self.discountLabel.text = model.lastminute_des;
        }
      
      • 因为我们的图片是从根据一个网址的字符串去网上下载的,所以我们导入了另一个第三方库UIImageView+WebCache 用于下载网上图片
        - (void)sd_setImageWithURL:(NSURL *)url
        {
        [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
        }
  4. 常规流程: 创建CollectionView, 遵守协议实现方法

推荐阅读更多精彩内容