GenericDataSource
一个用于 Swift 编写的通用小可重用组件,为 UITableView
/UICollectionView
的数据源实现。
功能
-
BasicDataSource
可以轻松地自动解包绑定模型到单元格。 -
SegmentedDataSource
可以轻松构建分段控件或用于您的UICollectionView
/UITableView
数据源的空状态。 -
CompositeDataSource
可以使用易于使用的组件构建复杂的单元格/模型结构(BasicDataSource
SegmentedDataSource
或其他CompositeDataSource
)。 - 支持
UICollectionView
补充视图、UITableView
标题视图和页脚视图。 - 能够从
UIKit
类中覆盖任何数据源方法。 - 全面的单元测试覆盖率。
- 完整文档
需求
- iOS 8.0+
- Xcode 10
- Swift 4.0+
安装
CocoaPods
要使用CocoaPods将GenericDataSource
集成到您的Xcode项目中,请在您的Podfile
中指定它
pod 'GenericDataSources'
重要提醒:Pod的名称是GenericDataSource,结尾有“s”。
Carthage
要使用Carthage将GenericDataSource集成到您的Xcode项目中,请在您的Cartfile中指定它
github "GenericDataSource/GenericDataSource"
手动操作
将GenericDataSource.xcodeproj
通过拖动的方式添加到您的项目文件中。
然后您可以参考将现有的框架添加到项目中。
示例
基本数据源示例
UITableView
创建一个基本的数据源并将其绑定到表格视图。
let dataSource = BasicBlockDataSource<Example, BasicTableViewCell>() { (item: Example, cell: BasicTableViewCell, indexPath) -> Void in
cell.titleLabel?.text = item.title
}
// Need to keep a strong reference to our data source.
self.dataSource = dataSource
// register the cell
tableView.ds_register(cellClass: BasicTableViewCell.self)
// bind the data source to the table view
tableView.ds_useDataSource(dataSource)
dataSource.items = <<retrieve items>> // Can be set and altered at anytime
就是这样!您已经实现了第一个数据源。无需排队!无需转换!简单而智能。
UICollectionView
现在,让我们将这个概念提升到下一个层次。假设在实现它之后,需求发生了变化,我们需要使用 UICollectionView
来实现它。
let dataSource = BasicBlockDataSource<Example, BasicCollectionViewCell>() { (item: Example, cell: BasicCollectionViewCell, indexPath) -> Void in
cell.titleLabel?.text = item.title
}
// Need to keep a strong reference to our data source.
self.dataSource = dataSource
// register the cell
collectionView.ds_register(cellClass: BasicCollectionViewCell.self)
// bind the data source to the collection view
collectionView.ds_useDataSource(dataSource)
dataSource.items = <<retrieve items>> // Can be set and altered at anytime
您所需要做的,仅仅是将细胞类以及当然的表格视图改为集合视图。
实际上,这为许多可能性打开了大门。您可以继承自 BasicDataSource
并实现一个基于由细胞实现的协议的自定义通用数据源,这样您就无需重复配置部分。您将创建这样的数据源。
let dataSource1 = CustomDataSource<BasicTableViewCell>() // for table view
let dataSource2 = CustomDataSource<BasicCollectionViewCell>() // for collection view
App store Featured Example
假设我们想实现以下屏幕,App StoreFeatured标签页。
如果您想查看完整的源代码,可以在Example项目下找到,即 AppStoreViewController.swift
。
- 我们将创建单元格,就像我们通常做的那样。
- 现在,我们需要考虑数据源。
- 很简单,每个细胞类型一个数据源(
BasicDataSource
)。 - 对于具有不同细胞类型的表格视图行,使用
CompositeDataSource(sectionType: .single)
。 - 使用
SegmentedDataSource
在加载视图和数据视图之间切换。 - 将
SegmentedDataSource
数据源绑定到表格视图,这样就完成了。 - 看我们是如何从结构上思考我们的UI和数据源,而不是一个大的单元格。
我们还没有讨论到特色部分单元格的 UICollectionView
。它非常简单,就是 BasicDataSource
。
看我们如何在以下代码中实现屏幕
- 创建单元格。
class AppStoreFeaturedSectionTableViewCell: UITableViewCell { ... }
class AppStoreQuickLinkLabelTableViewCell: UITableViewCell { ... }
class AppStoreQuickLinkTableViewCell: UITableViewCell { ... }
class AppStoreFooterTableViewCell: UITableViewCell { ... }
class AppStoreLoadingTableViewCell: UITableViewCell { ... }
- 创建
BasicDataSource
。
class AppStoreLoadingDataSource: BasicDataSource<Void, AppStoreLoadingTableViewCell> {
// loading should take full screen size.
override func ds_collectionView(_ collectionView: GeneralCollectionView, sizeForItemAt indexPath: IndexPath) -> CGSize {
return collectionView.size
}
}
class AppStoreFooterDataSource: BasicDataSource<Void, AppStoreFooterTableViewCell> { ... }
class AppStoreQuickLinkDataSource: BasicDataSource<FeaturedQuickLink, AppStoreQuickLinkTableViewCell> { ... }
class AppStoreFeaturedAppsDataSource: BasicDataSource<FeaturedApp, AppStoreFeaturedAppCollectionViewCell> { ... }
class AppStoreFeaturedAppsSectionDataSource: BasicDataSource<FeaturedSection, AppStoreFeaturedSectionTableViewCell> { ... }
class AppStoreQuickLinkLabelDataSource: BasicDataSource<String, AppStoreQuickLinkLabelTableViewCell> { ... }
- 创建包含特色页面的
CompositeDataSource
。
class AppStoreFeaturedPageDataSource: CompositeDataSource {
init() { super.init(sectionType: .single)] }
var page: FeaturedPage? {
didSet {
// remove all existing data sources
removeAllDataSources()
guard let page = page else {
return
}
// add featured apps
let featuredApps = AppStoreFeaturedAppsSectionDataSource()
featuredApps.setSelectionHandler(UnselectableSelectionHandler())
featuredApps.items = page.sections
add(featuredApps)
// add quick link label
let quickLinkLabel = AppStoreQuickLinkLabelDataSource()
quickLinkLabel.setSelectionHandler(UnselectableSelectionHandler())
quickLinkLabel.items = [page.quickLinkLabel]
add(quickLinkLabel)
// add quick links
let quickLinks = AppStoreQuickLinkDataSource()
quickLinks.items = page.quickLinks
add(quickLinks)
// add footer
let footer = AppStoreFooterDataSource()
footer.setSelectionHandler(UnselectableSelectionHandler())
footer.items = [Void()] // we add 1 element to show the footer, 2 elements will show it twice. 0 will not show it.
add(footer)
}
}
}
- 创建最外层数据源。
class AppStoreDataSource: SegmentedDataSource {
let loading = AppStoreLoadingDataSource()
let page = AppStoreFeaturedPageDataSource()
// reload data on index change
override var selectedDataSourceIndex: Int {
didSet {
ds_reusableViewDelegate?.ds_reloadData()
}
}
override init() {
super.init()
loading.items = [Void()] // we add 1 element to show the loading, 2 elements will show it twice. 0 will not show it.
add(loading)
add(page)
}
}
- 注册单元格。
tableView.ds_register(cellNib: AppStoreFeaturedSectionTableViewCell.self)
tableView.ds_register(cellNib: AppStoreQuickLinkLabelTableViewCell.self)
tableView.ds_register(cellNib: AppStoreQuickLinkTableViewCell.self)
tableView.ds_register(cellNib: AppStoreFooterTableViewCell.self)
tableView.ds_register(cellNib: AppStoreLoadingTableViewCell.self)
- 将数据源设置到集合视图中。
tableView.ds_useDataSource(dataSource)
- 最后在数据就绪时设置数据。
// show loading indicator
dataSource.selectedDataSourceIndex = 0
// get the data from the service
service.getFeaturedPage { [weak self] page in
// update the data source model
self?.dataSource.page.page = page
// show the page
self?.dataSource.selectedDataSourceIndex = 1
}
这样做有很多好处
- 轻量级视图控制器。
- 您无需再考虑索引了,一切都被我们处理了。只需考虑如何将您的单元格结构化成更小的数据源。
- 我们可以轻松地在
UITableView
和UICollectionView
之间切换,无需修改数据源或模型。只需更改单元格以继承自UITableViewCell
或UICollectionViewCell
,其他一切都会正常运作。 - 我们可以轻松地添加/删除/更新单元格。例如,我们决定添加更多蓝色链接。我们可以通过只需向传递给数据源的数组中添加新项来完成此操作。
- 我们可以根据需要重新排列单元格。只需移动数据源调用中的
add
。 - 最重要的是,我们代码中没有
if
/else
。
查看示例应用程序以获取完整实现。
归属
主要理念来自 [WWDC 2014 高级用户界面与集合视图] (https://developer.apple.com/videos/play/wwdc2014/232/),用泛型编写的 Swift。
作者
Mohamed Afifi, [email protected]
许可协议
GenericDataSource 在 MIT 许可协议下可用。详情请参阅 LICENSE 文件。