截屏2020-07-16 下午3.27.12.png
-
一些應用需要如圖所示一樣在集合視圖上讓不同分區(qū)配置背景圖片或顏色。并且背景要占據分區(qū)頭的高度之類操作,尤其是電商平臺類別的應用窍蓝。但是集合視圖本身不支持直接設置背景圖片郊艘,經過一番研究膘掰,可以通過重寫UICollectionViewFlowLayout實現這個效果洁段。
IMG_0736.JPG 自定義UICollectionReusableView類晌柬,用來注冊section裝飾背景View
class SectionBackgroundReusableView: UICollectionReusableView {
static let BACKGAROUND_CID = "BACKGAROUND_CID"
private lazy var bg_imageView = UIImageView().then {
addSubview($0)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
bg_imageView.frame = bounds
guard let att = layoutAttributes as? SectionDecorationViewCollectionViewLayoutAttributes else {
return
}
self.backgroundColor = UIColor.clear
bg_imageView.layer.cornerRadius = 5
bg_imageView.clipsToBounds = true
bg_imageView.backgroundColor = att.backgroundColor
guard let imageName = att.imageName else {
self.bg_imageView.image = nil
return
}
guard let image_url = URL(string: imageName) else {
return
}
self.bg_imageView.kf.setImage(with: image_url)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- 自定義UICollectionViewLayoutAttributes,用來保存section的背景圖片和顏色數據
class SectionDecorationViewCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
// 裝飾背景圖片
var imageName: String?
// 背景色
var backgroundColor = UIColor.white
/// 所定義屬性的類型需要遵從 NSCopying 協(xié)議
/// - Parameter zone:
/// - Returns:
override func copy(with zone: NSZone? = nil) -> Any {
let copy = super.copy(with: zone) as! SectionDecorationViewCollectionViewLayoutAttributes
copy.imageName = self.imageName
copy.backgroundColor = self.backgroundColor
return copy
}
/// 所定義屬性的類型還要實現相等判斷方法(isEqual)
/// - Parameter object:
/// - Returns: 是否相等
override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? SectionDecorationViewCollectionViewLayoutAttributes else {
return false
}
if self.imageName != rhs.imageName {
return false
}
if !self.backgroundColor.isEqual(rhs.backgroundColor) {
return false
}
return super.isEqual(object)
}
}
- 自定義UICollectionViewFlowLayout類
- 先注冊背景View
override init() {
super.init()
// 背景View注冊
self.register(SectionBackgroundReusableView.self, forDecorationViewOfKind: SectionBackgroundReusableView.BACKGAROUND_CID)
}
- 設置背景的位置和大小
// 布局配置數據
override func prepare() {
super.prepare()
// 如果collectionView當前沒有分區(qū)垒探,則直接退出
guard let numberOfSections = self.collectionView?.numberOfSections
else {
return
}
// 不存在cardDecorationDelegate就退出
guard let delegate = decorationDelegate else {
return
}
if decorationBackgroundAttrs.count > 0 {
decorationBackgroundAttrs.removeAll()
}
for section:Int 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
}
var sectionInset:UIEdgeInsets = self.sectionInset
/// 獲取該section的內邊距
let inset:UIEdgeInsets = delegate.collectionView(collectionView: self.collectionView!, layout: self, insetForSectionAt: section)
if !(inset == .zero) {
sectionInset = inset
}
/// 獲取該section header的size
let headerSize = delegate.collectionView(collectionView: self.collectionView!, layout: self, headerForSectionAt: section)
var sectionFrame:CGRect = .zero
if self.scrollDirection == .horizontal {
let hx = (firstItem.frame.origin.x) - headerSize.width + sectionInset.left
let hy = (firstItem.frame.origin.y) + sectionInset.top
let hw = ((lastItem.frame.origin.x) + (lastItem.frame.size.width)) - sectionInset.right
let hh = ((lastItem.frame.origin.y) + (lastItem.frame.size.height)) - sectionInset.bottom
sectionFrame = CGRect(x: hx , y: hy, width: hw, height: hh)
sectionFrame.origin.y = sectionInset.top
sectionFrame.size.width = sectionFrame.size.width-sectionFrame.origin.x
sectionFrame.size.height = self.collectionView!.frame.size.height - sectionInset.top - sectionInset.bottom
} else {
let vx = (firstItem.frame.origin.x)
let vy = (firstItem.frame.origin.y) - headerSize.height + sectionInset.top
let vw = ((lastItem.frame.origin.x) + (lastItem.frame.size.width))
let vh = ( (lastItem.frame.origin.y) + (lastItem.frame.size.height) ) - sectionInset.bottom
sectionFrame = CGRect(x: vx , y: vy, width: vw, height: vh + 10)
sectionFrame.origin.x = sectionInset.left
sectionFrame.size.width = (self.collectionView?.frame.size.width)! - sectionInset.left - sectionInset.right
sectionFrame.size.height = sectionFrame.size.height - sectionFrame.origin.y
}
let attrs = SectionDecorationViewCollectionViewLayoutAttributes(forDecorationViewOfKind: SectionBackgroundReusableView.BACKGAROUND_CID, with: IndexPath(item: 0, section: section))
let backgroundColor = delegate.collectionView(self.collectionView!, layout: self, decorationColorForSectionAt: section)
attrs.frame = sectionFrame
attrs.zIndex = -1
attrs.backgroundColor = backgroundColor
/// 優(yōu)先保存顏色
self.decorationBackgroundAttrs[section] = attrs
/// 判斷背景圖片是否可見 不可見跳過
let backgroundDisplayed = delegate.collectionView(self.collectionView!, layout: self, decorationImgaeDisplayedForSectionAt: section)
guard backgroundDisplayed == true else {
continue
}
/// 如果背景圖片名稱為nil怠李,跳過
guard let imageName = delegate.collectionView(self.collectionView!, layout: self, decorationImageForSectionAt: section) else {
continue
}
attrs.imageName = imageName
let displayedFillet = delegate.collectionView(self.collectionView!, layout: self, filletDisplayedForSectionAt: section)
guard displayedFillet == false else {
continue
}
// 將該section的布局屬性保存起來
self.decorationBackgroundAttrs[section] = attrs
}
}
- 這個方法比較重要圾叼,collectionView的分區(qū)可以設置背景這是重點,重寫DecorationView捺癞。Decoration View是UICollectionView的裝飾視圖夷蚊,這是蘋果官方解釋,
// If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.
//如果布局支持任何補充或裝飾視圖類型髓介,則還應該為這些類型實現相應的atIndexPath:方法惕鼓。
override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let section = indexPath.section
if elementKind == SectionBackgroundReusableView.BACKGAROUND_CID {
return self.decorationBackgroundAttrs[section]
}
return super.layoutAttributesForDecorationView(ofKind: elementKind,
at: indexPath)
}
-
重要的代碼都做了解釋,如果剛好需要這個效果的唐础,可以下載代碼看看箱歧,再結合自身的需求進行修改矾飞。代碼地址在前面已經貼出來了
7a65dbe6e060de7e6629c85b06e4c82c.jpg