先說一下整體的邏輯:由于iOS
自帶的UICollectionViewFlowLayout
是不支持每一個(gè)section
帶有不同的樣式,所以要想實(shí)現(xiàn)這個(gè)就要自定義一個(gè)集成自UICollectionViewFlowLayout
的Layout
。下面來看具體的實(shí)現(xiàn):
1链峭、首先創(chuàng)建QSLayoutAttributes
類集成自UICollectionViewLayoutAttributes
袁串,從名字上看我們就知道這個(gè)類是存儲layout樣式屬性的類其掂,這里直接上代碼了:
import UIKit
class QSLayoutAttributes: UICollectionViewLayoutAttributes {
/// 添加組背景
var backgroundColor:UIColor?
/// 添加組圓角
var corners:UIRectCorner?
/// 添加組圓角的大小
var sectionCornerRadii:CGSize?
}
當(dāng)然我這里只是簡單的定義了幾個(gè)樣式的屬性靶累,如果需求可根據(jù)自身需求無線的擴(kuò)展敏释。
2碍彭、下面這個(gè)類QSReusableView
集成自UICollectionReusableView
這實(shí)際上是一個(gè)裝飾類晤硕,這里就是用這個(gè)來作為整個(gè)組的背景。
class QSReusableView: UICollectionReusableView {
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
if layoutAttributes.isKind(of: QSLayoutAttributes.self) {
let layoutAttribute = layoutAttributes as? QSLayoutAttributes
if layoutAttribute!.backgroundColor != nil {
self.backgroundColor = layoutAttribute!.backgroundColor
}
/// 默認(rèn)設(shè)置為 0 以后有需要可以自定義
if layoutAttribute!.corners != nil {
self.setRadius(0, withRectCorners: layoutAttribute!.corners!)
}
}
}
}
3庇忌、那么重點(diǎn)來了舞箍,直接上代碼
import UIKit
let kDefaultCornerRadii = CGSize(width: 0, height: 0)
class QSCollectionViewLayout: UICollectionViewFlowLayout {
/// 存儲添加的屬性
private var layoutAttributes:Array<UICollectionViewLayoutAttributes>?
/// CollectionView的邊距 這個(gè)值可以自定義 默認(rèn)是0
public var marginValue:CGFloat = 0
override init() {
super.init()
self.layoutAttributes = []
/// 注冊一個(gè)修飾View,這個(gè)UIView就是用來做樣式展示的
self.registDecorationView()
}
func registDecorationView() {
self.register(QSReusableView.self, forDecorationViewOfKind: QSReusableView.className())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - override
extension QSCollectionViewLayout{
// NOTE: 該方法會在你每次刷新collection data的時(shí)候都會調(diào)用
override func prepare() {
super.prepare()
/// 避免屬性重復(fù)添加數(shù)據(jù)過大
self.layoutAttributes?.removeAll()
/// 設(shè)置背景和圓角
self.setSectionBackgaoundColorAndCorner()
}
/// 返回rect中的所有的元素的布局屬性
/// - Parameter rect: rect description
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var attributes = super.layoutAttributesForElements(in: rect)
for attribute in self.layoutAttributes! {
///判斷兩個(gè)區(qū)域是否有交集
if rect.intersects(attribute.frame){
attributes?.append(attribute)
}
}
return attributes
}
/// 給Decorationview返回屬性數(shù)組
override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
if elementKind == QSReusableView.className() {
return self.layoutAttributes![indexPath.section]
}
return super.layoutAttributesForDecorationView(ofKind: elementKind, at: indexPath)
}
}
// MARK: - 設(shè)置屬性
extension QSCollectionViewLayout{
/// 給collectionView設(shè)置背景和圓角
func setSectionBackgaoundColorAndCorner(){
/// 獲取collectionView的代理
guard let delegate = self.collectionView?.delegate else {
return
}
/// 沒遵守這個(gè)協(xié)議就不再往下設(shè)置
if delegate.conforms(to: QSCollectionViewDelegateFlowLayout.self) == false {
return
}
/// collectionView有多少組
let numberOfSections = self.collectionView?.numberOfSections ?? 0
if numberOfSections == 0 {
return
}
/// 循環(huán)遍歷各組 設(shè)置添加的屬性
for section in 0..<numberOfSections {
/// 一組cell的Item
let numberOfItems = self.collectionView?.numberOfItems(inSection: section) ?? 0
if (numberOfItems <= 0) {
continue;
}
/// 每一組第一個(gè)item的Attributes
let firstItem = self.layoutAttributesForItem(at: IndexPath.init(item: 0, section: section))
/// 每一組最后一個(gè)item的Attributes
let lastItem = self.layoutAttributesForItem(at: IndexPath.init(item: numberOfItems - 1, section: section))
/// 滿足條件 結(jié)束本次循環(huán)執(zhí)行下一次
if ((firstItem == nil) || (lastItem == nil)) {
continue
}
/// 實(shí)現(xiàn)了insetForSectionAt
if delegate.responds(to: #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:insetForSectionAt:))) {
//邊距
let inset = (delegate as? UICollectionViewDelegateFlowLayout)? .collectionView?(self.collectionView!, layout: self, insetForSectionAt: section)
self.sectionInset = inset!
}
/// 獲取第一個(gè)和最后一個(gè)item的聯(lián)合frame 皆疹,得到的就是這一組的frame
var sectionFrame:CGRect = firstItem!.frame.union(lastItem!.frame)
/// 設(shè)置它的x.y 注意理解這里的x點(diǎn)和y點(diǎn)的坐標(biāo)疏橄,不要硬搬,下面這樣寫的時(shí)候是把inset的left的
/// 距離包含在sectionFrame 開始x的位置 下面的y同邏輯
sectionFrame.origin.x -= self.sectionInset.left - self.marginValue
sectionFrame.origin.y -= self.sectionInset.top
///橫向滾動
if self.scrollDirection == .horizontal{
/// 計(jì)算組的寬的時(shí)候要把縮進(jìn)進(jìn)去的距離加回來 因?yàn)榭s進(jìn)是內(nèi)容縮進(jìn)
sectionFrame.size.width += self.sectionInset.left + self.sectionInset.right
/// 橫向滾動的時(shí)候 組的高就是collectionView的高
sectionFrame.size.height = self.collectionView!.frame.size.height
/// 縱向滾動
}else{
/// 縱向滾動的時(shí)候組的寬度 這里的道理和上面的x,y的一樣略就,需要你按照自己項(xiàng)目的實(shí)際需求去處理
sectionFrame.size.width = self.collectionView!.frame.size.width - (2 * self.marginValue)
sectionFrame.size.height += self.sectionInset.top + self.sectionInset.bottom
}
/// 根據(jù)自定義的CollectionViewSectionBackground 裝飾View初始化一個(gè)自定義的CollectionLayoutAttributes
let attribute = QSLayoutAttributes.init(forDecorationViewOfKind:QSReusableView.className(),with: IndexPath.init(item: 0, section: section))
attribute.frame = sectionFrame
attribute.zIndex = -1
/// 實(shí)現(xiàn)了backgroundColorForSection
if delegate.responds(to: #selector(QSCollectionViewDelegateFlowLayout.backgroundColorForSection(collectionView:layout:section:))){
/// 背景色
attribute.backgroundColor = (delegate as? QSCollectionViewDelegateFlowLayout)?.backgroundColorForSection!(collectionView: self.collectionView!, layout: self, section: section)
}
/// 實(shí)現(xiàn)了cornerForSection
if delegate.responds(to: #selector(QSCollectionViewDelegateFlowLayout.cornerForSection(collectionView:layout:section:))) {
/// 圓角
attribute.corners = (delegate as? QSCollectionViewDelegateFlowLayout)?.cornerForSection!(collectionView: self.collectionView!, layout: self, section: section)
/// 要是是默認(rèn)的大小就不在實(shí)現(xiàn)cornerRadiiForSection
attribute.sectionCornerRadii = kDefaultCornerRadii;
}
/// 要是自定義了圓角大小
if delegate.responds(to: #selector(QSCollectionViewDelegateFlowLayout.cornerRadiiForSection(collectionView:layout:section:))) { attribute.sectionCornerRadii = (delegate as? QSCollectionViewDelegateFlowLayout)?.cornerRadiiForSection!(collectionView: self.collectionView!, layout: self, section: section)
}
self.layoutAttributes?.append(attribute)
}
}
}
//使用協(xié)議來設(shè)置具體的樣式
@objc protocol QSCollectionViewDelegateFlowLayout:UICollectionViewDelegateFlowLayout{
/// CollectionView 的組設(shè)置背景顏色
/// - Returns: return 組的顏色
@objc optional func backgroundColorForSection(
collectionView:UICollectionView,
layout:UICollectionViewLayout,
section:NSInteger) -> UIColor
/// CollectionView 的組設(shè)置圓角
/// - Returns: UIRectCorner eg:[.topLeft,.topRight]
@objc optional func cornerForSection(
collectionView:UICollectionView,
layout:UICollectionViewLayout,
section:NSInteger) -> UIRectCorner
/// CollectionView 的組設(shè)置圓角的大小 要是默認(rèn)的0可不實(shí)現(xiàn)此方法返回
/// - Returns: CGSize 圓角大小
@objc optional func cornerRadiiForSection(
collectionView:UICollectionView,
layout:UICollectionViewLayout,
section:NSInteger) -> CGSize
}
4捎迫、基礎(chǔ)代碼就是這樣了,下面來怎么使用
//UICollectionView的定義
private lazy var collectionV:UICollectionView = {
let layout = QSCollectionViewLayout()
let _cv = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
_cv.delegate = self
_cv.dataSource = self
_cv.register(StatisticalCell.self, forCellWithReuseIdentifier: kCellId)
_cv.register(FeeStaisticalCell.self, forCellWithReuseIdentifier: kFeeCellId)
_cv.register(StatisticalHeaderView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: kHeaderId)
return _cv
}()
//實(shí)現(xiàn)協(xié)議表牢,需要哪些樣就實(shí)現(xiàn)哪些協(xié)議就好了
extension MyVC:QSCollectionViewDelegateFlowLayout{
//section的背景
func backgroundColorForSection(collectionView: UICollectionView, layout: UICollectionViewLayout, section: NSInteger) -> UIColor {
return UIColor(hexString: "ffffff")
}
//section的圓角
func cornerForSection(
collectionView:UICollectionView,
layout:UICollectionViewLayout,
section:NSInteger) -> UIRectCorner{
//列如只需要上方圓角
return [UIRectCorner.topLeft,UIRectCorner.topRight]
}
//圓角大小
func cornerRadiiForSection(
collectionView:UICollectionView,
layout:UICollectionViewLayout,
section:NSInteger) -> CGSize{
return CGSize(width: 10, height: 10)
}