UICollectionView-集合视图(理论篇 + 简单demo)
前言:
本文部分理论摘抄自网络(感谢各位大神的分享!),自己稍加整理,然后加上自己的理解再修饰。文章最后以示例代码的形式展示了怎么运用,用于备忘。
1. 概述:
UICollectionView 多列的UITableView(最简单的形式),类似于iBooks中书架的布局。
最简单的UICollectionView是一个GridView,可以多列的方式进行展示。
实现垂直方向的单列表来说,使用UITableView足以;若是需要构建横向滑动列表、gridView等直线型布局,则使用UICollectionView+UICollectionViewFlowLayout搭建最合适;更复杂的布局,则可以使用UICollectionView+自定义Layout来实现。
2. 组成
Cells:
用于展示内容主体:对不同的cell可以定制不同尺寸和不同内容。它是从数据源对象获取的
Supplementary Views:
追加视图: 类似于tableview中,每个Section的Header或者 Footer。它是从数据源方法当中获取的,但是与cell不同的是,它并不是强制需要的。
Decoration Views:
装饰视图:每个section的背景,它没有什么功能性,它不跟数据源有任何关系,它完全属于layout对象。
3. 工作原理
当UICollectionView显示内容时,先从dataSource数据源获取cell,然后交给UICollectionView。再从UICollectionViewLayout获取对应的layout attributes(布局属性)。最后,根据每个cell对应的layout attributes(布局属性)来对cell进行布局,生成了最终的界面。而用户交互的时候,都是通过Delegate来进行交互。当然,上面只是布局cell,但是UICollectionView内部还有Supplementary View和Decoration View,也可以对其进行布局。
4. 使用基本步骤
1) viewController:UICollectionViewDataSource, UICollectionViewDelegate 遵循这两个协议
2) 注册复用的cell(必须) 及 supplementaryView(如果需要)
3) 实现UICollectionViewDataSource及UICollectionViewDelegate协议的方法
5. 复用cell及Supplementary View
5.1 注册
在使用数据源返回cell或者Supplementary View给collectionView之前,我们必须先要注册,用来进行重用。
registerClass:forCellWithReuseIdentifier:
registerNib:forCellWithReuseIdentifier:
registerClass:forSupplementaryViewOfKind:withReuseIdentifier:
registerNib:forSupplementaryViewOfKind:withReuseIdentifier:
显而易见,前面两个方法是注册cell,后两个方法注册Supplementary View。其中,注册的方式有两种,第一种是直接注册class,这样的初始化方法创建cell;另外一种是注册nib,它会自动加载nib文件。
注册示例代码如下:
self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier:”cell”)
5.2 获取
在数据源方法当中返回cell或者Supplementaryview的方法当中通过
dequeueReusableCellWithReuseIdentifier:forIndexPath:
dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:
方法获取cell或者SupplementaryView。
6. dataSource-数据源方法
数据源方法与UITableView类似,主要有:
numberOfSectionsInCollectionView://集合共有多少个部分
collectionView:numberOfItemsInSection://每个部分有多少个元素
collectionView:cellForItemAtIndexPath://复用cell
collectionView: viewForSupplementaryElementOfKind:atIndexPath: //复用supplementrayViews
与UITableView不同的是多加了返回Supplementary view数据源方法。
7. delegate-代理方法
数据源为UICollectionView提供数据相关的内容,而代理则主要负责用户交互、与数据无关的视图外形。主要分成两部分:
7.1 通过调用代理方法,管理视图的选中、高亮
-1.collectionView:shouldHighlightItemAtIndexPath: //是否应该高亮?
-2.collectionView:didHighlightItemAtIndexPath: //如果1回答为是,那么高亮
-3.collectionView:shouldSelectItemAtIndexPath: //无论1结果如何,都询问是否可以被选中?
-4.collectionView:didUnhighlightItemAtIndexPath: //如果1回答为是,那么现在取消高亮
-5.collectionView:didSelectItemAtIndexPath:// 如果3回答为是,那么选中cell
7.2 长按cell,显示编辑菜单
与UITableView不同,用户长按cell时,UICollectionView可以显示编辑菜单。这个编辑菜单可以用来剪切、复制和粘贴cell。不过,要显示这个编辑菜单需要满足下面几个条件:
代理对象必须实现下面三个方法:
collectionView:shouldShowMenuForItemAtIndexPath:
collectionView:canPerformAction:forItemAtIndexPath:withSender:
collectionView:performAction:forItemAtIndexPath:withSender:
对于指定要编辑的cell,collectionView:shouldShowMenuForItemAtIndexPath:方法需要返回YES
collectionView:canPerformAction:forItemAtIndexPath:withSender: 方法中,对于剪切、复制、粘贴三种action至少有一个返回YES。其实,编辑菜单是有很多种action的,但是对于UICollectionView来说,它仅仅支持的剪切、复制、粘贴三个,所以说这个代理方法至少支持这三种的一种。
剪切、复制、粘贴的方法名是: cut: copy: paste:
如下图所示:
当上面的条件都满足了,用户就可以长按cell显示出编辑菜单,然后选择对应的action,从而就会回调delegate的collectionView:performAction:forItemAtIndexPath:withSender: 方法去做对应的事情。
当我们想控制编辑菜单仅仅显示复制和粘贴时,我们就可以用collectionView:canPerformAction:forItemAtIndexPath:withSender: 方法阻止剪切按钮出现在编辑菜单里。
func collectionView(collectionView: UICollectionView, canPerformAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) -> Bool
{
print("action:\(NSStringFromSelector(action))")
if action == "cut:" {
return false
}
return true
}
隐藏cut效果图如下:
8. Demo (纯代码实现)
运行效果图:
说明:图中红色的为header,橙色的为footer,详情见代码定义。
//
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
//定义集合视图
var collectionView: UICollectionView!
//定义元素标示
let cellIndentifier = "cell"
override func viewDidLoad() {
super.viewDidLoad()
//设置布局方式为flowLayout
let layout = UICollectionViewFlowLayout()
//设置item之间的距离
layout.minimumInteritemSpacing = 15
//设置section边界
layout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
//设置item行距
layout.minimumLineSpacing = 28
//设置supplementaryView header的尺寸, 不设置则不会显示header
layout.headerReferenceSize = CGSize(width: view.bounds.size.width, height: 10)
//设置supplementaryView footer的尺寸,不设置则不会显示footer
layout.footerReferenceSize = CGSize(width: view.bounds.size.width, height: 10)
self.collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height), collectionViewLayout: layout)
//设置代理
self.collectionView.delegate = self
//设置数据源
self.collectionView.dataSource = self
//注册复用的元素
self.collectionView.registerClass(UICollectionViewCell.self , forCellWithReuseIdentifier: cellIndentifier)
//注册header self.collectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind:UICollectionElementKindSectionHeader , withReuseIdentifier: "headView")
//注册footer self.collectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind:UICollectionElementKindSectionFooter , withReuseIdentifier: "footView")
// self.collectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind:"customView" , withReuseIdentifier: "customView")
self.collectionView.backgroundColor = UIColor.grayColor()
self.view.addSubview(collectionView)
}
//MARK: UICollectionViewDataSource实现方法
//配置每个部分(section)有多少个元素(item)
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
//配置集合视图(collectionView)有多少个部分(section)
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// 取得元素
let cell = self.collectionView.dequeueReusableCellWithReuseIdentifier(cellIndentifier, forIndexPath: indexPath)
//配置元素
cell.layer.masksToBounds = true
// cell.layer.cornerRadius = 25
//取得随机颜色:颜色值在0到1至之间,除以255.0为了得到小数
cell.backgroundColor = UIColor(red: CGFloat(Double(random() % 255) / 255.0),
green: CGFloat(Double(arc4random() % 255) / 255.0),
blue: CGFloat(Double(arc4random() % 255) / 255.0),
alpha: 1)
return cell
}
//配置supplementaryView
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
var view: UICollectionReusableView! = nil
if kind == UICollectionElementKindSectionHeader {
view = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "headView", forIndexPath: indexPath)
view.backgroundColor = UIColor.redColor()
} else if kind == UICollectionElementKindSectionFooter {
view = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "footView", forIndexPath: indexPath)
view.backgroundColor = UIColor.orangeColor()
}
return view
}
// MARK: UICollectionViewDelegate
// Uncomment this method to specify if the specified item should be highlighted during tracking
func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool
{
return true
}
//执行高亮显示
func collectionView(collectionView: UICollectionView, didHighlightItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
cell?.contentView.backgroundColor = UIColor.whiteColor()
}
//取消高亮显示
func collectionView(collectionView: UICollectionView, didUnhighlightItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
cell?.contentView.backgroundColor = nil
}
// Uncomment this method to specify if the specified item should be selected
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool
{
return true
}
//- ----配置编辑菜单-------------------
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
func collectionView(collectionView: UICollectionView, shouldShowMenuForItemAtIndexPath indexPath: NSIndexPath) -> Bool
{
return true
}
func collectionView(collectionView: UICollectionView, canPerformAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) -> Bool
{
print("action:\(NSStringFromSelector(action))")
if action == "cut:" {
return false
}
return true
}
func collectionView(collectionView: UICollectionView, performAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) {
print("performAction------")
}
}