14°

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。

 

本文转载自博客园,原文链接:https://www.cnblogs.com/zhangxiaoxu/p/11540362.html

全部评论: 0

    我有话说: