Swift - 給UICollectionview設定組背景和圓角
鍾情圓角怎麼辦
最近由於我們的UI鍾情於圓角搞得我很方,各種圓角漸變,於是就有了下面這篇給UICollection組設定圓角和背景色的誕生,不知道在我們平時有沒有遇到這樣子的一些需求,就是按照每一組給UIColllectionView設定不同的背景色,要是沒有遇到的同學建議可以先思考一下改怎麼處理在看下面的內容吧。
首先需要考慮的是在哪裡設定了,我們都應該知道關於CollectionView的屬性幾乎都是在Layout裡面在管理的,那我們要給它設定背景色和院圓角也肯定是在在這去寫的了,在大小間距等佈局方面我們遵循的都是 UICollectionViewDelegateFlowLayout 這個代理,但這時候我們就應該想到這個DelegateFlowLayout裡面沒有設定背景色和圓角的代理的,我們需要背景色這個概念的話就只能去註冊一個修飾View然後給修飾的View去設定背景色和圓角了。
// MARK: - 註冊一個裝飾View func registClass() { self.register(PPReusableView.self, forDecorationViewOfKind: PPCollectionViewSectionBackground) }
NOTE: PPReusableView.self 這個語法在OC中就等於[PPReusableView Class]
PPReusableView是繼承與UICollectionReusableView這個裝飾View,我們後面會說這個View 後面的 PPCollectionViewSectionBackground 就是我們平時像註冊cell時候的一個 identify 而已。
重點
在我們寫瀑布流或者別的一些佈局的時候,我們都是在哪裡重寫的? 沒錯就是 prepare 方法, 我把這整個方法全都放出來看,註釋寫的很仔細的,要是有不理解的地方可以再留言Q我,具體的肯定是我們繼承 UICollectionViewFlowLayout 寫了,這裡需要注意UICollectionViewFlowLayout和UICollectionViewDelegateFlowLayout,別搞混淆了。按照如下定義一個PPBaseFlowLayout繼承與UICollectionViewFlowLayout,在裡面我們重寫prepare這個方法。
// MARK: - 重寫 - prepare // NOTE: 該方法會在你每次重新整理collection data 的時候都會呼叫 override func prepare() { super.prepare() self.layoutAttributes?.removeAll() /// 有多少組 let numberOfSections = self.collectionView?.numberOfSections ?? 0 let delegate = self.collectionView?.delegate /// 不存在就直接返回 沒法再往下設定 guard (numberOfSections != 0) || !(delegate is PPCollectionViewDelegateFlowLayout) else { return } // 迴圈遍歷各組 設定新增的屬性 for section in 0..<numberOfSections { /// 一組的Item let numberOfItems = self.collectionView?.numberOfItems(inSection: section) if (numberOfItems! <= 0) { continue; } /// 每一組第一個item的Attributes let firstItem = self.layoutAttributesForItem(at: IndexPath.init(item: 0, section: section)) /// 每一組最後一個item的Attributes let lastItem = self.layoutAttributesForItem(at: IndexPath.init(item: numberOfItems! - 1, section: section)) /// 滿足條件 結束本次迴圈執行下一次 if ((firstItem == nil) || (lastItem == nil)) { continue } if(delegate?.responds(to:#selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:insetForSectionAt:))))! { let inset = (delegate as? UICollectionViewDelegateFlowLayout)? .collectionView?(self.collectionView!, layout: self, insetForSectionAt: section) self.sectionInset = inset! } /// 獲取第一個和最後一個item的聯合frame ,得到的就是這一組的frame var sectionFrame:CGRect = firstItem!.frame.union(lastItem!.frame) /// 設定它的x.y sectionFrame.origin.x -= self.sectionInset.left - 10 sectionFrame.origin.y -= self.sectionInset.top ///橫向滾動 if self.scrollDirection == .horizontal{ /// 計算組的寬的時候要把縮排進去的距離加回來 因為縮排是內容縮排 sectionFrame.size.width += self.sectionInset.left + self.sectionInset.right /// 橫向滾動的時候 組的高就是collectionView的高 sectionFrame.size.height = self.collectionView!.frame.size.height /// 縱向滾動 }else{ /// 縱向滾動的時候組的寬度 sectionFrame.size.width = self.collectionView!.frame.size.width-20 sectionFrame.size.height += self.sectionInset.top + self.sectionInset.bottom } /// 根據自定義的PPCollectionViewSectionBackground 裝飾View初始化一個自定義的PPLayoutAttributes let attribute = PPLayoutAttributes.init(forDecorationViewOfKind:PPCollectionViewSectionBackground,with: IndexPath.init(item: 0, section: section)) attribute.frame = sectionFrame attribute.zIndex = -1 /// 背景色 attribute.backgroundColor = (delegate as? PPCollectionViewDelegateFlowLayout)?.backgroundColorForSection!(collectionView: self.collectionView!, layout: self, section: section) /// 圓角 attribute.corners = (delegate as? PPCollectionViewDelegateFlowLayout)?.sessionBackgroundViewCornerscollectionView!(collectionView: self.collectionView!, layout: self, section: section) self.layoutAttributes?.append(attribute) } }
NOTE:仔細看程式碼可以看到圓角和背景色的屬性都是設定給PPLayoutAttributes,這玩意又是什麼呢?就是我們CollectionView的屬性管理者UICollectionViewLayoutAttributes,你進UICollectionViewLayoutAttributes可以看到它的屬性有那些,不要忘記我們是根據修飾View初始化得到這個屬性的,按照正常的操作我們會在最後返回一個屬性陣列,自定義過collection佈局的應該清楚一些,具體的PPCollectionViewDelegateFlowLayout就是我們繼承與UICollectionViewDelegateFlowLayout寫的代理了,這個代理裡面也就兩個方法,圓角和顏色的設定,程式碼如下:
// MARK: - 可以在協議裡面新增代理方法用於設定你想給CollectionView新增的屬性設定值 @objc protocol PPCollectionViewDelegateFlowLayout:UICollectionViewDelegateFlowLayout{ /// 給CollectionView 的組設定背景顏色 /// /// - Parameters: /// - collectionView: collectionView description /// - layout: layout description /// - section: section description /// - Returns: return value description @objc optional func backgroundColorForSection(collectionView:UICollectionView, layout:UICollectionViewLayout, section:NSInteger) -> UIColor /// 給CollectionView 的組設定圓角 /// /// - Parameters: /// - collectionView: collectionView description /// - layout: layout description /// - section: section description /// - Returns: UIRectCorner eg:[.topLeft,.topRight] @objc optional func sessionBackgroundViewCornerscollectionView(collectionView:UICollectionView, layout:UICollectionViewLayout, section:NSInteger) -> UIRectCorner }
說說這個PPLayoutAttributes吧
這個還真沒法仔細說,因為....... 下面就是它的全部程式碼
import Foundation import UIKit // MARK: - 這裡可以給CollectionView 新增自定義屬性 class PPLayoutAttributes: UICollectionViewLayoutAttributes { var backgroundColor:UIColor? var corners:UIRectCorner? }
不能忘記了PPReusableView,它的程式碼也比較的簡單,如下
import Foundation import UIKit // MARK: - 可重複使用檢視 class PPReusableView: UICollectionReusableView { override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { super.apply(layoutAttributes) if layoutAttributes.isKind(of: PPLayoutAttributes.self) { let layoutAttribute = layoutAttributes as? PPLayoutAttributes if layoutAttribute!.backgroundColor != nil { self.backgroundColor = layoutAttribute!.backgroundColor } /// 預設設定為 12 以後有需要可以自定義 if layoutAttribute!.corners != nil { self.setRoundingCorners(layoutAttribute!.corners!, cornerRadii: kDefaultCornerRadii) } } } }
可以了返回你的屬性吧
隨後就是返回你前面設定了那麼多的屬性,具體的就是下面的程式碼所示了
// MARK: - 重寫 - layoutAttributesForElements 返回各組的屬性 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var attributes = super.layoutAttributesForElements(in: rect) /// for attribute in self.layoutAttributes! { ///判斷兩個區域是否相交 if rect.intersects(attribute.frame){ attributes?.append(attribute) } } return attributes } // MARK: - 重寫 - layoutAttributesForDecorationView 給裝飾的View返回屬性 /// Decorationview 裝飾view override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { if elementKind == PPCollectionViewSectionBackground { return self.layoutAttributes![indexPath.section] } return super.layoutAttributesForDecorationView(ofKind: elementKind, at: indexPath) }
最後:
最後我們在最前面說的registClass這個方法我們在PPBaseFlowLayout的初始化方法裡面呼叫就可以了,還有屬性陣列這寫就不用說了吧還是在前面自己定義初始化了。
然後上面程式碼裡面的一些巨集定義比如identify還有圓角大小等等這些就根據自己的需求去寫吧。
最後在初始化CollectionView的時候layout就是我們定義的PPBaseFlowLayout了,遵守的代理就是PPCollectionViewDelegateFlowLayout,這個需要留意下就OK。
&n