2025.02.05 工作变动原因,故将一些工作期间Tapd内部写的Wiki文档转移到个人博客。
一个通用的标签瀑布流单选器(自定义选项样式、统一的点击切换处理)。
小组件单选器依赖于 iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一) 一文中的base组件。
一、需求
如UI设计图所示,需要一个横向的标签瀑布流的切换组件。
第一时间想到以前写过一个瀑布流的base组件(iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一)),如果以之为基础组件,就可以快速实现一个通用的标签单选器了!这样在以后遇到相同功能,不同样式的需求,就可以节省大量的重复开发时间。
二、技术设计思路
第一,需要一个能生成通用瀑布流单选器布局的组件。
第二,可以支持灵活自定义比较重要的字段 (选中文本颜色、选中文本背景颜色、未选中文本颜色、未选中背景颜色、字体、间距 等等)
第三,自动处理选中事件的样式,返回点击选中的回调。
总结,因为前文已经拥有了(第一)和(第二点)大部份功能了,后面只需要做一些小细节上的处理就可以了。!!
三、单选器小组件源码
BaseView
在这段代码中,除去前文已经完成的基础功能外,添加的小细节有:
-
var clickTag: ((YAYBaseTagModel) -> ())?
点击标签回调,让业务处理本身的点击事件。
// 瀑布单选器切换BaseView(横向单行文字类型)
import UIKit
class YAYTagSwitchView: YAYBaseTagView {
/// 点击标签回调
var clickTag: ((YAYBaseTagModel) -> ())?
/// 被选中cell的index
var selecedIndex: Int = 0
override init(frame: CGRect, option: YAYBaseTagOption) {
super.init(frame: frame, option: option)
layout.scrollDirection = .horizontal
backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func getSizeForItem(_ tagModel: YAYBaseTagModel, collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let tagModel = dataArray[indexPath.row]
let text = "\(tagModel.labelName)"
let width: CGFloat = text.textWidth(fontSize: option.fontSize, height: option.titleHeight, fontWeight: option.fontWeight) + option.itemLeftMargin + option.itemRightMargin
// 超过限制,文本宽度要恢复才能出现...
return CGSize(width: min(option.contentWidth - 5, width), height: option.itemHeight)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: option.cellClass, for: indexPath) as? YAYTagSwitchCell ?? YAYTagSwitchCell()
// 选中状态
cell.isSelected = (selecedIndex == indexPath.row)
// 处理样式
cell.tagOptionSet(option: option)
// 绑定数据
cell.bindData(model: dataArray[indexPath.row])
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
super.collectionView(collectionView, didSelectItemAt: indexPath)
selecedIndex = indexPath.row
self.reloadData()
// 选择标签切换回调
clickTag?(dataArray[indexPath.row])
}
}
BaseOption配置类
YAYBaseTagOption
的具体配置在 iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一) 文中已经介绍过了。
在通用的标签瀑布选择器配置类 YAYTagSwitchViewOption
中主要添加了三个可自定义的选中配置。
-
selectedItemTextColor
选中标题颜色 -
selectedItemBackgourdColor
选中背景颜色 -
selectedTitleFont
选中文字
以下是配置类源码:
// 瀑布单选器切换配置类(横向单行文字类型)
import Foundation
class YAYTagSwitchViewOption: YAYBaseTagOption {
/// 选中标题颜色
var selectedItemTextColor: UIColor = .white
/// 选中背景颜色
var selectedItemBackgourdColor: UIColor = .white
/// 选中文字
var selectedTitleFont: UIFont?
init(leftM: CGFloat = 15, rightM: CGFloat = 15, contentWidth: CGFloat = screenWidth) {
super.init()
self.leftM = leftM
self.rightM = rightM
self.contentWidth = contentWidth - leftM - rightM
cellClass = "YAYTagSwitchCell"
topM = 10
titleHeight = 22
itemHeight = 22
itemCorner = 2
minimumLineSpacing = 10
minimumInteritemSpacing = 10
fontSize = 12
itemLeftMargin = 8
itemRightMargin = 8
itemBackGroundColor = .hexColor(hex: "#F7F7F7")
itemTextColor = subContentColor
selectedItemBackgourdColor = .hexColor(hex: "#EEF8E8")
selectedItemTextColor = .hexColor(hex: "#449F11")
}
}
BaseCell 元素
YAYTagSwitchCell
主要是根据单选器配置类,自动处理选中与未选中的样式,绑定标签文本。
// 瀑布单选器切换cell(横向单行文字类型)
import UIKit
class YAYTagSwitchCell: YAYBaseTagCell {
// MARK: - bindData
override func tagOptionSet(option: YAYBaseTagOption) {
super.tagOptionSet(option: option)
// snapKit约束,文本居中
titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.right.equalToSuperview()
make.height.equalTo(option.titleHeight)
}
guard let tagOption = option as? YAYTagSwitchViewOption else { return }
// 切换选中颜色
if isSelected {
titleLabel.textColor = tagOption.selectedItemTextColor
contentView.backgroundColor = tagOption.selectedItemBackgourdColor
if tagOption.selectedTitleFont != nil {
titleLabel.font = tagOption.selectedTitleFont ?? UIFont.systemFont(ofSize: 14, weight: .medium)
}
}
else {
titleLabel.textColor = tagOption.itemTextColor
contentView.backgroundColor = tagOption.itemBackGroundColor
if tagOption.titleFont != nil {
titleLabel.font = tagOption.titleFont ?? UIFont.systemFont(ofSize: 14)
}
}
}
override func bindData(model: YAYBaseTagModel) {
super.bindData(model: model)
titleLabel.text = model.labelName
}
}
四、具体使用
1. 初始化
YAYTagSwitchView(frame: CGRect(x: 0, y: 44, width: screenWidth, height: 44), option: YAYTagSwitchViewOption())
2. 加载标签标题数组
YAYTagSwitchView().reload(array: [
YAYBaseTagModel(labelId: 0, labelName: "报名中", labelTimes: 0),
YAYBaseTagModel(labelId: 1, labelName: "进行中", labelTimes: 0),
YAYBaseTagModel(labelId: 2, labelName: "已结束", labelTimes: 0),
])
3. 具体效果
4. 加载标签标题数组和点击事件处理
tagView.clickTag = { [unowned self] tagModel in
if tagModel.labelId == 0 {
// 处理事件
// 埋点
}
else {
// 处理事件
// 埋点
}
}
五、相同功能新业务开发
具体效果如下图所示:
这是一个功能一样,但样式稍微不同(字体、weight、item大小)的单选器,这时候只需要新建一个 YAYWelfareInfoTagViewOption
新的配置类即可完成所有开发工作,实现同样如上一个一样的瀑布流单选器。
class YAYWelfareInfoTagViewOption: YAYTagSwitchViewOption {
override init(leftM: CGFloat = 15, rightM: CGFloat = 15, contentWidth: CGFloat = screenWidth) {
super.init()
self.leftM = leftM
self.rightM = rightM
self.contentWidth = contentWidth - leftM - rightM
cellClass = "YAYTagSwitchCell"
titleHeight = 22
itemHeight = 22
itemCorner = 2
minimumLineSpacing = 10
minimumInteritemSpacing = 10
fontSize = 12
itemLeftMargin = 8
itemRightMargin = 8
itemBackGroundColor = .hexColor(hex: "#F7F7F7")
itemTextColor = subContentColor
selectedItemBackgourdColor = .hexColor(hex: "#EEF8E8")
selectedItemTextColor = .hexColor(hex: "#449F11")
}
}
然后在需要引用的地方,新建一个 lazy var tagView: YAYTagSwitchView
,添加到业务的VC中 view.addSubview(tagView)
即可,实际代码如下:
最后,又双叒叕完成了一个开箱即用的瀑布流单选器通用小组件,以后类似的场景就可以直接引用该组件了。