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
}
外部调用方法极其简单,传入一个
listView
和scrollDelegate
即可完成初始化
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的数据模型类,其中包含了
BWListRegister
、BWListSection
、BWListItem
、BWListSectionLayout
、BWListHeaderFooter
、BWListItemAction
-
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
}
}
}
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)"
}
}
注意
- 使用的cell为
storyboard
上的tableView/collectionView
里时,BWListData.registers
里不需要添加对应的BWListRegtister
对象,只需要保持items
里的resuseId
和cell
的一致性即可:
listAdapter.data = .init(sections: [
.init(items: (0..<10).map{
.init(reuseId: TestCell.RID, height: 54, data: $0, action: nil)
})
])