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

2025.02.05 工作变动原因,故将一些工作期间Tapd内部写的Wiki文档转移到个人博客。

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

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

一、需求

tapd_44062861_1701246650_612.jpg

如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 中主要添加了三个可自定义的选中配置。

  1. selectedItemTextColor 选中标题颜色
  2. selectedItemBackgourdColor 选中背景颜色
  3. 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. 具体效果

tapd_44062861_1701246650_612.jpg

4. 加载标签标题数组和点击事件处理

tagView.clickTag = { [unowned self] tagModel in
    if tagModel.labelId == 0 {
        // 处理事件
        // 埋点
    }
    else {
        // 处理事件
        // 埋点
    }
}

五、相同功能新业务开发

具体效果如下图所示:

tapd_44062861_1701249008_535.jpg

这是一个功能一样,但样式稍微不同(字体、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) 即可,实际代码如下:

截屏2025-02-06 01.01.26.png

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

最最最后,完结撒花

告辞.jpeg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容