iOS | BWListKit 列表視圖開發(fā)神器

Github

https://github.com/BackWorld/BWListKit

簡(jiǎn)介

BWListKit 是基于UITableView/UICollectionView封裝了各自的delegate、datasource協(xié)議方法,提供了基于數(shù)據(jù)驅(qū)動(dòng)的列表構(gòu)建方式,采用Adapter適配器大大減少了代碼量,提高了開發(fā)效率,提高了封裝性和統(tǒng)一性。

核心類

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類中存儲(chǔ)了外部用戶的tableView和collectionView,動(dòng)態(tài)進(jìn)行了cell、header、footer的注冊(cè),及數(shù)據(jù)加載

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

外部調(diào)用方法極其簡(jiǎn)單,傳入一個(gè)listViewscrollDelegate即可完成初始化

BWListAdapter+CollectionView/TableView

該extension中主要實(shí)現(xiàn)了UICollection/TableView的代理方法,進(jìn)行了cell、header、footer的設(shè)置,及點(diǎn)擊等action方法的實(shí)現(xiàn)和設(shè)置

  • 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中實(shí)現(xiàn)了列表的滾動(dòng)代理UIScrollViewDelegate方法

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

BWListData

該類為BWListView的數(shù)據(jù)模型類,其中包含了BWListRegisterBWListSection、BWListItemBWListSectionLayoutBWListHeaderFooter、BWListItemAction

  • BWListRegister xib、class注冊(cè)模型
  • BWListSection 分組模型
  • BWListSectionLayout 分組布局模型
  • BWListHeaderFooter 分組頭部模型
  • BWListItem cell模型
  • BWListItemAction cell的action(如點(diǎn)擊等)模型

示例

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需實(shí)現(xiàn)BWListItemView協(xié)議
class STRefundDetailDoingCell: UITableViewCell, BWListItemView {
    
    @IBOutlet var dots: [UIView]!
    @IBOutlet var lines: [UIView]!
    @IBOutlet var labels: [UILabel]!
    // Data為用戶自定義數(shù)據(jù)結(jié)構(gòu)
    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里時(shí),BWListData.registers里不需要添加對(duì)應(yīng)的BWListRegtister對(duì)象,只需要保持items里的resuseIdcell的一致性即可:
listAdapter.data = .init(sections: [
            .init(items: (0..<10).map{
                .init(reuseId: TestCell.RID, height: 54, data: $0, action: nil)
            })
        ])
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容