iOS设置UICollectionView的Section的背景色和背景图

我正在参加「掘金·启航计划」

效果图

WechatIMG21.jpeg

最近遇到这样一个类似于支付宝应用中心功能模块,UICollectionView点击增删、拖拽、排序功能。先不提其他的,这里出现了UICollectionViewSection设置了背景色??

WeChatce8088d69a9da0852739cdae7505d915.jpg


虽然有段时间没写iOS了,但是没听说UICollectionView有设置Section背景色的属性啊?问了下,原来组员是用UIScrollView + UIView 实现的,不雅实属不雅,有点low low的。笔者就试试用UICollectionView来实现次功能,盲猜要自定义一大堆东西了。

sectionInset

在此之前我们要简单了解下这个属性:sectionInset:设置Section内间距

简单用代码运行下:

let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 50, right: 10)
layout.headerReferenceSize = CGSize(width: kWidth, height: 40)
layout.footerReferenceSize = CGSize(width: kWidth, height: 40)

为了更直观添加了区头和区尾,运行后:

WechatIMG17_副本.jpeg

看图可知:sectionInsetPadding不一样,是设置Section内间距不是外间距。而且也看得出来UICollectionView的默认每个Section都是相邻的,相互之间并没有间距。


思路

据上所述,我们可以知道UICollectionView的每个Section之间是没有间距的,而且UICollectionView默认是完全没有SectionviewcontentView,或者说压根就没有区的相关UI一说。

没有也没事,没有我们可以自己造,让SectionDecoration view,可以让其设置UI属性。

Section 和 Decoration view ,相当于 Cell 和 contentView 的关系。

这样思路就来了:

  • 继承 UICollectionViewLayoutAttributes自定义布局属性,添加需要的UI属性,笔者这里添加了backgroundColor(背景色)、imageName(网路和本地图片,根据hasPrefix("http")判断)、cornerRadius(圆角)。

  • 继承UICollectionReusableView,自定义添加一个UIImageView作为Section的装饰背景图,之所以选择UIImageView是因为既可以满足添加背景色也可以添加背景图片。

  • 自定义UICollectionViewFlowLayout,重写prepare布局,计算每个Decoration view的位置,并返回相关的UI属性。

  • 仿照UICollectionView的代理,新增一个协议sectionDelegate,使用代理来设置每个Section的不同属性。

下面一个个实现。
工程文件:

Snip20230616_3.png

SectionDecorationFlowLayoutDelegate

是仿照UICollectionView的代理,新增的代理协议sectionDelegate,代码:

// 设置Section背景色,默认:white
func collectionView(_ collectionView:UICollectionView,layoutcollectionViewLayout: SectionDecorationFlowLayout,backgroundColorForSectionAt section:Int) -> UIColor

// 设置Section的背景图片,默认:nil(简单判断下:根据是否含http判断是网络图片还是本地图片)
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: SectionDecorationFlowLayout,backgroundImageForSectionAt section: Int) -> String?

// 设置Section背景圆角,默认:nil
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: SectionDecorationFlowLayout,cornerRadiusForSectionAt section: Int) -> CGFloat?

// 设置Section背景的外间距,默认:UIEdgeInsets.zero
func collectionView(collectionView: UICollectionView,layout collectionViewLayout: SectionDecorationFlowLayout,marginForSectionAt section: Int) -> UIEdgeInsets


// 设置Section Header 宽高,默认:CGSizeZero
func collectionView(collectionView: UICollectionView,layout collectionViewLayout:SectionDecorationFlowLayout,headerForSectionAt section: Int) -> CGSize

// 设置Section Footer 宽高,默认:CGSizeZero
func collectionView(collectionView: UICollectionView,layout collectionViewLayout:SectionDecorationFlowLayout,footerForSectionAt section: Int) -> CGSize

然后再扩展下,设置每个属性的默认值。
这里笔者除了添加背景色、背景图片、圆角,还额外设置了3个其他的属性,这3个重点说下。

marginForSection:外间距

由上可知UICollectionView的每个Section之间是没有间距的,而且默认我们添加的每个SectionDecoration view相互之间也是没有间距。这时候可以添加一个 外间距margin 属性,可以设置每个Section的外间距,配合UICollectionViewsectionInset属性来实现我们想要的效果。

注意一:Decoration是真实的Section区域通过margin内减的得到的。比如真实的Section的宽高为:100×100,设置margin的top、left、bottom、right的值均为10,则计算后Section的Decoration宽高为:80×80。

注意二:
所以若添加了区头或区尾,注意添加子控件的尺寸,在添加了margin下,显示效果可能出现区头或区尾的子控件位置超出Decoration,这点注意下就行。

headerForSection、footerForSection

因为可能存在区头或区尾,且它们的宽高可以通过headerReferenceSizefooterReferenceSize设置,也可以通过代理来设置,而且可能每个Section存在不同的情况,因此我们仿照UICollectionViewDelegate,通过代理获取每个区头、区尾的尺寸,参与计算Decoration view的尺寸。

SectionDecorationLayoutAttributes

这个没什么好说的,UICollectionViewLayoutAttributes是管理指定元素的布局属性的对象,可以使用该对象中的布局属性来控制cell和Supplementary View的位置。

新增imageName、backgroundColor、cornerRadius属性,所定义属性的类型需要遵从 NSCopying 协议。然后重写下 copyisEqual 方法就结束了。

SectionDecorationReusableView

在这里自定义UICollectionReusableView,新增一个UIImageView来作为Decoration view。重写 apply 方法,通过UICollectionViewLayoutAttributes拿到对应Section的数据,给UIImageView设置相应的属性。

SectionDecorationFlowLayout

这个是重点了,重写 prepare 方法,计算每个Section的位置。计算位置前需要区分scrollDirection这里就以垂直方向为例说明。

// 获取以下数据:
firstItem            // 第一个Item
lastItem             // 最后一个Itme
margin               // 根据代理获取section的外边距
headerSize           // 根据代理获取section header的size
footerSize           // 根据代理获取section footer的size
collectionInset      // collectionView的contentInset 

let x = margin.left

let y = firstItem.frame.origin.y - sectionInset.top - collectionInset.top + margin.top - headerSize.height


let w = self.collectionView!.frame.size.width - collectionInset.left - collectionInset.right - margin.left - margin.right

let h = CGRectGetMaxY(lastItem.frame) - firstItem.frame.origin.y + sectionInset.top + sectionInset.bottom + headerSize.height + footerSize.height  - margin.top - margin.bottom

decorationFrame = CGRect(x: x, y: y, width: w, height: h)

contentInset 是最大的限制,所有的布局都会受到限制。

Decoration view默认填充整个真实的Section区域,因此它只受到 contentInsetmargin 的限制,不受 sectionInset 内间距 的影响,只是通过 sectionInset 来计算Decoration view的宽高。

位置计算好后,通过代理获取之前设置的3个属性值,赋值给SectionDecorationLayoutAttributes对象后保存。

水平方向计算方式差不多,只是需要找到Section中X坐标最大的item,遍历下就行。

最后一定要重写 layoutAttributesForDecorationView 方法。

结束

到此,设置UICollectionView的Section的背景色和背景图功能也就完成了,看起来很多,实际上按照步骤一步步实现也不是很难,而且使用起来也很简单,重点是通过 contentInsetsectionInsetmargin 组合设置实现自己需要效果。布局也建议按照这个顺序来设置,可以先设置一个看效果,再考虑是否设置另一个。具体的实现这里就不展开说了。


代码在下面,有需要的可以自取。

RCSectionDecoration

若存在什么不对的地方,欢迎指正!

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYbzU55b' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片