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)
}
}
|