UICollectionView的使用之开篇

一、UICollectionView介绍

UICollectionView是用来展示一系列数据的视图,相较于UITableView,其允许我们实现自定义的排版布局,例如实现画廊、瀑布流、横向Banner等效果。

image.png

二、UICollectionView的使用Demo1

UICollectionView的使用方法也类似UITableView,我们只需通过数据源UICollectionViewDatasource提供显示的数据,通过实现UICollectionViewDelegate的代理方法处理点击事件等。

下面我们来创建个简单的动物世界Demo,最终效果如图:


最终效果

先新建cell,继承自UICollectionViewCell,并在cell中添加一个UIImageView


新建Cell

添加UIImageView

先贴出UICollectionView的完整代码,再做详细讲解,代码如下:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: 100, height: 100)
        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.dataSource = self
        collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
        view.addSubview(collectionView)
    }
}

extension ViewController: UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCell
        cell.imageView.image = UIImage(named: "\(indexPath.row)")
        return cell
    }
}

下面我们详细介绍其中的方法:

1、Layout

let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)

Layout是UICollectionView的核心,其定义了每个Cell、头视图、尾视图在UICollectionView中的布局(尺寸和位置),正常需通过创建UICollectionViewLayout的子类,实现对应方法。但如果只是想实现简单的水平或垂直的网格排布,可以像例子中直接用苹果以及为我们准备好的UICollectionViewFlowLayout即可。例子中将每个cell的大小设置为100*100,

1、Cell

collectionView.dataSource = self
collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")

UICollectionView的每一个数据,对应着一个UICollectionViewCell,与UITableview相同,为了减少系统资源开销,苹果也是使用了重用机制,避免大量创建视图,但不同于UITableView可以在cellforRow方法中手动创建UITableViewCell,在UICollectionView中只能预先通过以下了两种方法注册cell:

func register(AnyClass?, forSupplementaryViewOfKind: String, withReuseIdentifier: String)
func register(UINib?, forCellWithReuseIdentifier: String)

3、DataSource

当UICollectionView被初次加载时,会请求数据源获取当前可见的Cell和额外视图,在对应的数据源方法中通过dequeueReusableCell方法从可重用池中获取之前已预先注册的cell:

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCell
        cell.imageView.image = UIImage(named: "\(indexPath.row)")
        return cell
    }

三、Supplementary view

cell还可以分为成多个组(section),每个组可以设置头视图(section header)和尾视图(section footer),头视图和尾视图统称为额外视图(supplementary view),如下是苹果官方示例图


苹果官方图

现在我们也为动物世界Demo添加上头视图,显示分组,最终效果如图:


demo2

先新建ZoneCollectionReusableView,继承自UICollectionReusableView,并在ZoneCollectionReusableView中添加一个UILabel和UIView分别显示标题和分割线

新建header
添加子视图

照例先上代码,这次代码是在前面的基础上添加,只列出新增代码及其前一行


class ViewController: UIViewController {

    override func viewDidLoad() {
        ...
        layout.itemSize = CGSize(width: 100, height: 100)
        layout.headerReferenceSize = CGSize(width: view.bounds.size.width, height: 44)
        ...
        collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
        collectionView.register(UINib(nibName: "ZoneCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ZoneCollectionReusableView")
        ...
    }
}

extension ViewController: UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 3
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 6
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ZoneCollectionReusableView", for: indexPath) as! ZoneCollectionReusableView
        header.nameLabel.text = "第\(indexPath.section+1)组"
        return header
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
     ...
    }
layout.headerReferenceSize = CGSize(width: view.bounds.size.width, height: 44)

同理,由layout设置header view的大小布局,更多可设置属性如下:

@property(nonatomic) UICollectionViewScrollDirection scrollDirection
grid的滑动方向
 
@property(nonatomic) CGFloat minimumLineSpacing
每行之间最小的距离
 
@property(nonatomic) CGFloat minimumInteritemSpacing 
每一行中每个item间的最小的距离
 
@property(nonatomic) CGSize itemSize
每一个items的默认大小
 
@property(nonatomic) CGSize estimatedItemSize
每一个items的估计大小(iOS8之后的属性,只要设置了estimatedItemSize,collection view 就会根据 cell 里面的 autolayout 约束去确定cell 的大小)
 
@property(nonatomic) UIEdgeInsets sectionInset
section里面内容的页边空白
 
@property(nonatomic) CGSize headerReferenceSize
section头部的默认大小
 
@property(nonatomic) CGSize footerReferenceSizesection
section尾部的默认大小
collectionView.register(UINib(nibName: "ZoneCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ZoneCollectionReusableView")

预先注册视图,以便请求数据源方法时,通过dequeueReusableSupplementaryView方法返回重用视图。

func numberOfSections(in collectionView: UICollectionView) -> Int {
      return 3
}

数据源方法中,numberOfSections返回3表示有3个分组,每个分组有6个cell

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
      let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ZoneCollectionReusableView", for: indexPath) as! ZoneCollectionReusableView
      header.nameLabel.text = "第\(indexPath.section+1)组"
      return header
}

以上方法通过dequeueReusableSupplementaryView获取之前注册的视图并返回。

四、更多

这篇只是开篇,接下来的文章将介绍UICollectionView的进阶用法,如实现瀑布流、Banner、画廊等。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容