iOS | BWListKit 列表视图开发神器

Github

https://github.com/BackWorld/BWListKit

简介

BWListKit 是基于UITableView/UICollectionView封装了各自的delegate、datasource协议方法,提供了基于数据驱动的列表构建方式,采用Adapter适配器大大减少了代码量,提高了开发效率,提高了封装性和统一性。

核心类

BWListAdapter

class BWListAdapter: NSObject {
    weak var tableView: UITableView?
    weak var collectionView: UICollectionView?
    weak var scrollDelegate: BWListScrollDelegate?
    
    var data: BWListData? = nil {
        didSet{
            _registerTableView()
            _registerCollectionView()
            
            tableView?.reloadData()
            collectionView?.reloadData()
        }
    }

Adapter类中存储了外部用户的tableView和collectionView,动态进行了cell、header、footer的注册,及数据加载

convenience init(tableView: UITableView? = nil,
                     collectionView: UICollectionView? = nil,
                     scrollDelegate: BWListScrollDelegate? = nil) {
        self.init()
        
        if tableView == nil && collectionView == nil {
            fatalError("必须提供一个UITableView或UICollectionView!")
        }
        else if tableView != nil && collectionView != nil{
            fatalError("只能绑定一个UITableView或UICollectionView!")
        }
        
        tableView?.dataSource = self
        tableView?.delegate = self
        collectionView?.dataSource = self
        collectionView?.delegate = self
        
        self.tableView = tableView
        self.collectionView = collectionView
        self.scrollDelegate = scrollDelegate
    }

外部调用方法极其简单,传入一个listViewscrollDelegate即可完成初始化

BWListAdapter+CollectionView/TableView

该extension中主要实现了UICollection/TableView的代理方法,进行了cell、header、footer的设置,及点击等action方法的实现和设置

  • CollectionView
extension BWListAdapter: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let sections = data!.sections!
        let item = sections[indexPath.section].items![indexPath.item]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: item.reuseId, for: indexPath)
        
        if let proxy = cell as? BWListItemView {
            proxy.bwListItemViewConfigure(item.data, indexPath: indexPath)
        }
                
        return cell
    }
}
  • TableView
extension BWListAdapter: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let sections = data!.sections!
        let item = sections[indexPath.section].items![indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: item.reuseId) ?? tableView.dequeueReusableCell(withIdentifier: item.reuseId, for: indexPath)
        
        if let proxy = cell as? BWListItemView {
            proxy.bwListItemViewConfigure(item.data, indexPath: indexPath)
        }
                
        return cell
    }
}
  • ScrollView

该extension中实现了列表的滚动代理UIScrollViewDelegate方法

extension BWListAdapter: UIScrollViewDelegate{
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        scrollDelegate?.bwListScrollViewDidScroll(scrollView)
    }
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        scrollDelegate?.bwListScrollViewDidEndDecelerating(scrollView)
    }
}

BWListData

该类为BWListView的数据模型类,其中包含了BWListRegisterBWListSectionBWListItemBWListSectionLayoutBWListHeaderFooterBWListItemAction

  • BWListRegister xib、class注册模型
  • BWListSection 分组模型
  • BWListSectionLayout 分组布局模型
  • BWListHeaderFooter 分组头部模型
  • BWListItem cell模型
  • BWListItemAction cell的action(如点击等)模型

示例

UITableView

@IBOutlet weak var tableView: UITableView!
    private lazy var listAdapter = BWListAdapter(tableView: tableView)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        reloadData()
    }

private func reloadData() {
        let doingCellRID = STRefundDetailDoingCell.RID
        let successCellRID = STRefundDetailSuccessCell.RID
        let refuseCellRID = STRefundDetailRefuseCell.RID
        let closeCellRID = STRefundDetailCloseCell.RID
        let historyCellRID = STRefundDetailHistoryCell.RID
        let goodsDoingCellRID = STRefundDetailGoodsDoingCell.RID
        let goodsExpressCellRID = STRefundDetailGoodsExpressCell.RID
        let goodsDeliverCellRID = STRefundDetailGoodsDeliverCell.RID
        
        typealias Data = RefundDetailCellData
        let autoHeight = UITableView.automaticDimension
        
        listAdapter.data = .init(registers: [
            doingCellRID, successCellRID, refuseCellRID,
            closeCellRID, historyCellRID, goodsDoingCellRID,
            goodsExpressCellRID, goodsDeliverCellRID
        ].map{
            .init(style: .cell, xib: $0)
        }, sections: [
            .init(items: [
                .init(reuseId: doingCellRID, height: 185, data: doingCellData),
                .init(reuseId: goodsDoingCellRID, height: autoHeight, data: doingCellData),
                .init(reuseId: goodsExpressCellRID, height: autoHeight, data: doingCellData),
                .init(reuseId: goodsDeliverCellRID, height: 138),
                .init(reuseId: successCellRID, height: 160, data: Data(time: "2021/03/04 12:34:56", process: 2)),
                .init(reuseId: closeCellRID, height: 84, data: Data(time: "2021/03/04 12:34:56", process: -1)),
                .init(reuseId: refuseCellRID, height: autoHeight, data: Data(time: "2021/03/04 12:34:56", process: 2, refuse: .init(reason: "商品退回后才能退款", desc: "商品退回后才能退款商品退回后才能退款品退回后才能退款", images: [
                    "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3408752957,1706848666&fm=26&gp=0.jpg",
                    "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201812%2F17%2F20181217014535_rdqtz.thumb.700_0.jpg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617519148&t=329b0af4fd7ef406f02c8d14639100ec",
                    "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic8.58cdn.com.cn%2Fzhuanzh%2Fn_v226a083c72cf844babc7bcf719b0cc0e6.jpg%3Fw%3D750%26h%3D0&refer=http%3A%2F%2Fpic8.58cdn.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617519148&t=9b307f38269ef9efe0cb95d746642169"
                ]))),
                .init(reuseId: historyCellRID, height: 56)
            ])
        ])
    }
  • Cell需实现BWListItemView协议
class STRefundDetailDoingCell: UITableViewCell, BWListItemView {
    
    @IBOutlet var dots: [UIView]!
    @IBOutlet var lines: [UIView]!
    @IBOutlet var labels: [UILabel]!
    // Data为用户自定义数据结构
    override func bwListItemViewConfigure(_ data: Any?, indexPath: IndexPath) {
        guard let data = data as? Data,
              data.process >= 0 else {
            return
        }
        
        for (i,v) in dots.enumerated() {
            let isSelected = i <= data.process
            v.backgroundColor = isSelected ? STColor.themColorRead : STColor.fontColorE
        }
        for (i,v) in lines.enumerated() {
            let isSelected = i <= (data.process-1)
            v.backgroundColor = isSelected ? STColor.themColorRead : STColor.fontColorE
        }
    }
}
UITableView示例

UICollectionView

import BWListKit

class DemoCollectionVC: UIViewController {

@IBOutlet public weak var collectionView: CollectionView!

public lazy var listAdapter: BWListAdapter = {
        return BWListAdapter(collectionView: collectionView)
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        makeListData()
    }
    
    func makeListData() {
        let items: [BWListItem] = (0..<100).map{
            .init(reuseId: CollectionCell.RID, width: 60, height: 60, data: $0)
        }
        
        listAdapter.data = .init(registers: [
            .init(style: .cell, class: CollectionCell.self)
        ], sections: [
            .init(layout: .init(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), minimumInteritemSpacing: 20, minimumLineSpacing: 20), items: items)
        ])
    }
}
class CollectionCell: UICollectionViewCell, BWListItemView {
    @IBOutlet weak var textLabel: UILabel!
    
    override func awakeFromNib() {
        textLabel.textAlignment = .center
        textLabel.frame = contentView.bounds
        textLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        textLabel.textColor = .red
        textLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold)
    }
    
    public func bwListItemViewConfigure(_ data: Any?, indexPath: IndexPath) {
        textLabel.text = "\(data as! Int)"
    }
}
CollectionView 示例

注意

  • 使用的cell为storyboard上的tableView/collectionView里时,BWListData.registers里不需要添加对应的BWListRegtister对象,只需要保持items里的resuseIdcell的一致性即可:
listAdapter.data = .init(sections: [
            .init(items: (0..<10).map{
                .init(reuseId: TestCell.RID, height: 54, data: $0, action: nil)
            })
        ])
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278