Swift - 让CollectionView里的Section分别设置不同的背景色
作者:hangge | 2017-11-08 08:10
我们知道想要给 UICollectionView 设置背景色只需要通过 backgroundColor 属性即可。但如果想让不同的 Section (分区)能显示不同的背景颜色,UICollectionView 本身就没有提供相关的属性或方法了。
(2)接着打开 StoryBoard,将 Collection View 的 Layout 设置成我们自定义的布局。
要实现这个效果,需要我们通过自定义布局来实现,下面通过样例进行演示。
1,实现原理
(1)想通过自定义布局实现 Section 背景色,需要创建如下三个类的子类:
- 继承 UICollectionReusableView 来自定义一个装饰视图(Decoration 视图),用来作为各个分组的背景视图(section's background view)。
- 继承 UICollectionViewLayoutAttributes 来自定义一个新的布局属性,里面添加一个backgroundColor 属性,用来表示 Section 的背景颜色。
- 继承 UICollectionViewFlowLayout(默认的 Flow 布局)来自定义一个新布局,在这里我们会计算及返回各个分组背景视图的布局属性(位置、尺寸、颜色)
(2)为方便使用我们还要新增一个协议方法,使得 section 背景色可以在外面通过数据源来设置(就像设置 Cell 视图那样)
2,效果图
通过自定义布局,Collection View 里每个 section 都显示不同颜色的背景。
3,样例代码
(1)自定义布局(SectionBgCollectionViewLayout.swift)
import UIKit //表示我们自定义的分区背景(装饰视图) private let SectionBg = "SectionBgCollectionReusableView" //增加自己的协议方法,使其可以像cell那样根据数据源来设置section背景色 protocol SectionBgCollectionViewDelegate: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, backgroundColorForSectionAt section: Int) -> UIColor } //定义一个UICollectionViewLayoutAttributes子类作为section背景的布局属性, //(在这里定义一个backgroundColor属性表示Section背景色) private class SectionBgCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes { //背景色 var backgroundColor = UIColor.white //所定义属性的类型需要遵从 NSCopying 协议 override func copy(with zone: NSZone? = nil) -> Any { let copy = super.copy(with: zone) as! SectionBgCollectionViewLayoutAttributes copy.backgroundColor = self.backgroundColor return copy } //所定义属性的类型还要实现相等判断方法(isEqual) override func isEqual(_ object: Any?) -> Bool { guard let rhs = object as? SectionBgCollectionViewLayoutAttributes else { return false } if !self.backgroundColor.isEqual(rhs.backgroundColor) { return false } return super.isEqual(object) } } //继承UICollectionReusableView来自定义一个装饰视图(Decoration 视图),用来作为Section背景 private class SectionBgCollectionReusableView: UICollectionReusableView { //通过apply方法让自定义属性生效 override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { super.apply(layoutAttributes) guard let attr = layoutAttributes as? SectionBgCollectionViewLayoutAttributes else { return } self.backgroundColor = attr.backgroundColor } } //自定义布局(继承系统内置的 Flow 布局) class SectionBgCollectionViewLayout: UICollectionViewFlowLayout { //保存所有自定义的section背景的布局属性 private var decorationViewAttrs: [UICollectionViewLayoutAttributes] = [] override init() { super.init() setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func awakeFromNib() { super.awakeFromNib() setup() } //初始化时进行一些注册操作 func setup() { //注册我们自定义用来作为Section背景的 Decoration 视图 self.register(SectionBgCollectionReusableView.classForCoder(), forDecorationViewOfKind: SectionBg) } //对一些布局的准备操作放在这里 override func prepare() { super.prepare() //如果collectionView当前没有分区,或者未实现相关的代理则直接退出 guard let numberOfSections = self.collectionView?.numberOfSections, let delegate = self.collectionView?.delegate as? SectionBgCollectionViewDelegate else { return } //先删除原来的section背景的布局属性 self.decorationViewAttrs.removeAll() //分别计算每个section背景的布局属性 for section in 0..<numberOfSections { //获取该section下第一个,以及最后一个item的布局属性 guard let numberOfItems = self.collectionView?.numberOfItems(inSection: section), numberOfItems > 0, let firstItem = self.layoutAttributesForItem(at: IndexPath(item: 0, section: section)), let lastItem = self.layoutAttributesForItem(at: IndexPath(item: numberOfItems - 1, section: section)) else { continue } //获取该section的内边距 var sectionInset = self.sectionInset if let inset = delegate.collectionView?(self.collectionView!, layout: self, insetForSectionAt: section) { sectionInset = inset } //计算得到该section实际的位置 var sectionFrame = firstItem.frame.union(lastItem.frame) sectionFrame.origin.x = 0 sectionFrame.origin.y -= sectionInset.top //计算得到该section实际的尺寸 if self.scrollDirection == .horizontal { sectionFrame.size.width += sectionInset.left + sectionInset.right sectionFrame.size.height = self.collectionView!.frame.height } else { sectionFrame.size.width = self.collectionView!.frame.width sectionFrame.size.height += sectionInset.top + sectionInset.bottom } //更具上面的结果计算section背景的布局属性 let attr = SectionBgCollectionViewLayoutAttributes( forDecorationViewOfKind: SectionBg, with: IndexPath(item: 0, section: section)) attr.frame = sectionFrame attr.zIndex = -1 //通过代理方法获取该section背景使用的颜色 attr.backgroundColor = delegate.collectionView(self.collectionView!, layout: self, backgroundColorForSectionAt: section) //将该section背景的布局属性保存起来 self.decorationViewAttrs.append(attr) } } //返回rect范围下所有元素的布局属性(这里我们将自定义的section背景视图的布局属性也一起返回) override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var attrs = super.layoutAttributesForElements(in: rect) attrs?.append(contentsOf: self.decorationViewAttrs.filter { return rect.intersects($0.frame) }) return attrs } //返回对应于indexPath的位置的Decoration视图的布局属性 override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { //如果是我们自定义的Decoration视图(section背景),则返回它的布局属性 if elementKind == SectionBg { return self.decorationViewAttrs[indexPath.section] } return super.layoutAttributesForDecorationView(ofKind: elementKind, at: indexPath) } }
(2)接着打开 StoryBoard,将 Collection View 的 Layout 设置成我们自定义的布局。
(3)最后让 Collection View Controller 实现我们定义的 SectionBgCollectionViewDelegate 协议,返回每个 Section 的背景色即可。
import UIKit //重用单元格identifier private let cellIdentifier = "Cell" class CollectionViewController: UICollectionViewController, SectionBgCollectionViewDelegate { override func viewDidLoad() { super.viewDidLoad() //注册可重用的单元格 self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier) //将collectionView背景色设为白色 self.collectionView?.backgroundColor = UIColor.white } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } //返回分区数 override func numberOfSections(in collectionView: UICollectionView) -> Int { return 4 } //返回每个分区下单元格个数 override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { //奇数section里有8个单元格,偶数section里有4个单元格 return (section % 2 == 1) ? 4 : 8 } //返回每个单元格视图 override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) cell.backgroundColor = UIColor.white return cell } //返回每个分区的内边距 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { let numberOfItems = collectionView.numberOfItems(inSection: section) return numberOfItems > 0 ? UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) : UIEdgeInsets.zero } //返回每个分区的背景色 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, backgroundColorForSectionAt section: Int) -> UIColor { if section == 0 { return UIColor.green } else if section == 1 { return UIColor.cyan } else if section == 2 { return UIColor.blue } return UIColor.purple } }源码下载:hangge_1844.zip
全部评论(0)