一个通用的标签瀑布流多选器(自定义选项样式、统一的点击切换处理)。
小组件多选器依赖于 iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一) 一文中的base组件。
一、需求
如设计图所示,需要一个瀑布流的标签多选组件。
之前已经写过一个瀑布流单选器(iOS 小组件 - 瀑布流单选器,技术设计与实现详解(二)),以同样的思路写一个新的多选器。
二、技术设计思路
-
第一,需要一个能生成通用瀑布流多选器布局的组件。
-
第二,可以支持灵活自定义比较重要的字段 (选中文本颜色、选中文本背景颜色、未选中文本颜色、未选中背景颜色、字体、间距 等等)
-
第三,自动处理选中与反选事件的样式,返回点击选中的回调。
-
总结,因为前文已经拥有了(第一)和(第二点)大部份功能了,后面只需要做一些小细节上的处理就可以了。!!
三、多选器小组件源码
BaseView
在这段代码中,除去前文已经完成的基础功能外,添加的小细节有:
var hasSelecedClosure: ((Bool) -> ())?
点击标签回调,判断是否已选够目标个数(可下一步)。
// 瀑布标签多选器
import UIKit
class YAYTagMultipleChoiceView: YAYBaseTagView {
/// 是否已选够目标个数(可下一步)
var hasSelecedClosure: ((Bool) -> ())?
/// 限制最少选中个数
var leastChoiceCount: Int = 1
override init(frame: CGRect, option: YAYBaseTagOption) {
super.init(frame: frame, option: option)
layout.scrollDirection = .vertical
backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 获取已选择标签
func getSelectedTags() -> ([YAYBaseTagModel]) {
var selectedTags = [YAYBaseTagModel]()
for tag in dataArray {
// 如果记录了true,即是选中状态
if tag.isSelected ?? false {
selectedTags.append(tag)
}
}
return selectedTags
}
override func getSizeForItem(_ tagModel: YAYBaseTagModel, collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let tagModel = dataArray[indexPath.row]
let text = "\(tagModel.labelName)"
// 加5是因为算出来的宽度不够字体显示,有误差
let width: CGFloat = text.textWidth(fontSize: option.fontSize, height: option.titleHeight, fontWeight: option.fontWeight) + option.itemLeftMargin + option.itemRightMargin + 5
// 超过限制,文本宽度要恢复才能出现...
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? YAYTagMultipleChoiceCell ?? YAYTagMultipleChoiceCell()
cell.isSelected = dataArray[indexPath.row].isSelected ?? false
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)
// 数据源记录,选中与反选
dataArray[indexPath.row].isSelected = !(dataArray[indexPath.row].isSelected ?? false)
self.reloadData()
// 是否已选够目标个数
if hasSelecedClosure != nil {
hasSelecedClosure?(getSelectedTags().count >= leastChoiceCount)
}
}
}
BaseOption配置类
YAYBaseTagOption
的具体配置在 iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一) 文中已经介绍过了。
在通用的瀑布流多选器配置类 YAYTagMultipleChoiceOption
中主要添加了五个可自定义的选中配置。
selectedItemTextColor
选中标题颜色selectedItemBackgourdColor
选中背景颜色borderColor
边框颜色selectedBorderColor
选中边框颜色borderWidth
边框大小
以下是配置类源码:
// 瀑布标签多选配置
import UIKit
class YAYTagMultipleChoiceOption: YAYBaseTagOption {
/// 选中标题颜色
var selectedItemTextColor: UIColor = themeColor
/// 选中背景颜色
var selectedItemBackgourdColor: UIColor = themeColor.withAlphaComponent(0.08)
/// 边框颜色
var borderColor: CGColor = themeColor.withAlphaComponent(0.12).cgColor
/// 选中边框颜色
var selectedBorderColor: CGColor = themeColor.cgColor
/// 边框大小
var borderWidth: CGFloat = 1
init(leftM: CGFloat = 24, rightM: CGFloat = 24, contentWidth: CGFloat = screenWidth) {
super.init()
self.leftM = leftM
self.rightM = rightM
self.contentWidth = contentWidth - leftM - rightM
cellClass = "YAYTagMultipleChoiceCell"
topM = 25
titleHeight = 22
itemHeight = 46
itemCorner = 23
minimumLineSpacing = 16
minimumInteritemSpacing = 16
fontSize = 16
fontWeight = .medium
itemLeftMargin = 24
itemRightMargin = 24
itemBackGroundColor = .white
itemTextColor = contentColor
}
}
BaseCell 元素
YAYTagMultipleChoiceCell
主要是根据多选器配置类,自动处理选中与未选中的样式,绑定标签文本。
// 瀑布标签多选cell
import UIKit
class YAYTagMultipleChoiceCell: YAYBaseTagCell {
// MARK: - bindData
override func tagOptionSet(option: YAYBaseTagOption) {
super.tagOptionSet(option: option)
guard let tagOption = option as? YAYTagMultipleChoiceOption else { return }
titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(tagOption.itemLeftMargin)
make.right.equalToSuperview().offset(-tagOption.itemRightMargin)
make.height.equalTo(option.titleHeight)
}
// 切换选中状态
if isSelected {
titleLabel.textColor = tagOption.selectedItemTextColor
contentView.backgroundColor = tagOption.selectedItemBackgourdColor
contentView.layer.borderColor = tagOption.selectedBorderColor
contentView.layer.borderWidth = tagOption.borderWidth
contentView.layer.masksToBounds = true
}
else {
titleLabel.textColor = tagOption.itemTextColor
contentView.backgroundColor = tagOption.itemBackGroundColor
contentView.layer.borderColor = tagOption.borderColor
contentView.layer.borderWidth = tagOption.borderWidth
contentView.layer.masksToBounds = true
}
}
override func bindData(model: YAYBaseTagModel) {
super.bindData(model: model)
titleLabel.text = model.labelName
}
}
四、具体使用与效果
实际使用
类似前文标签iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一)、iOS 小组件 - 瀑布流单选器,技术设计与实现详解(二)例子一样,根据不同的 YAYTagMultipleChoiceOption
即可生成功能相同,样式不同的多选器,这里业务只做了一种样式的瀑布流多选器,故没有更多的使用例子。
具体多选效果
实际运行中,选够了3个最低个数后,可进行下一步。
最后,又双叒叕完成了一个开箱即用的瀑布流多选器通用小组件,以后类似的场景就可以直接引用该组件了。