iOS 小组件 - 瀑布流多选器,技术设计与实现详解(三)

85 阅读4分钟

一个通用的标签瀑布流多选器(自定义选项样式、统一的点击切换处理)。

小组件多选器依赖于 iOS 小组件 - 标签瀑布流Base组件抽取,APP业务重构(一) 一文中的base组件。

一、需求

IMG_8753.PNG

如设计图所示,需要一个瀑布流的标签多选组件。

之前已经写过一个瀑布流单选器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 中主要添加了五个可自定义的选中配置。

  1. selectedItemTextColor 选中标题颜色
  2. selectedItemBackgourdColor 选中背景颜色
  3. borderColor 边框颜色
  4. selectedBorderColor 选中边框颜色
  5. 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 即可生成功能相同,样式不同的多选器,这里业务只做了一种样式的瀑布流多选器,故没有更多的使用例子。

截屏2025-02-07 10.18.50.png

具体多选效果

实际运行中,选够了3个最低个数后,可进行下一步。

IMG_8754.PNG

最后,又双叒叕完成了一个开箱即用的瀑布流多选器通用小组件,以后类似的场景就可以直接引用该组件了。

最最最后,完结撒花

告辞.jpeg