问题描述
在大多数应用程序中都可以找到的著名布局是,在一个表格视图单元格中有几个水平列表,每个列表从服务器获取其数据。可以在Airbnb中找到。下面的示例:
每个列表仅在第一次创建网络请求时触发该网络请求,因此当通过滚动表格视图再次显示该列表时,它不应发出另一个网络请求以获取数据。
我尝试了几种方法,但还不满意。具有多个collectionview时,其中一些会遇到内存泄漏和性能问题。当前,我在保存表视图的View控制器中执行网络请求,并将数据传递到每个单元格。
解决方法
这是一个很大的问题,有很多不同的答案。我最近通过使用自定义表格视图数据源解决了该问题,该数据源报告单元格第一次(并且仅第一次)报告一个项目。我用它触发何时应该发生各个内部网络请求。它使用在RxSwiftExt ...中实现的.distinct()
运算符...
final class CellReportingDataSource<Element>: NSObject,UITableViewDataSource,RxTableViewDataSourceType where Element: Hashable {
let cellCreated: Observable<Element>
init(createCell: @escaping (UITableView,IndexPath,Element) -> UITableViewCell) {
self.createCell = createCell
cellCreated = _cellCreated.distinct()
super.init()
}
deinit {
_cellCreated.onCompleted()
}
func tableView(_ tableView: UITableView,observedEvent: Event<[Element]>) {
if case let .next(sections) = observedEvent {
self.items = sections
tableView.reloadData()
}
}
func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
let cell = createCell(tableView,indexPath,item)
_cellCreated.onNext(item)
return cell
}
private var items: [Element] = []
private let createCell: (UITableView,Element) -> UITableViewCell
private let _cellCreated = PublishSubject<Element>()
}
每个表视图单元格都需要有自己的Observable,每当有订阅者时,该Observable就会发出网络调用的结果。我通过使用.scan(into:accumulator:)
来做到这一点。一个例子可能是这样的:
dataSource.cellCreated
.map { ($0,/* logic to convert a table view item into network call parameters */) }
.flatMap {
Observable.zip(
Observable.just($0.0),networkCall($0.1)
.map { Result.success($0) }
.catchError { Result.failure($0) }
)
}
.scan(into: [TableViewItem: Result<NetworkResponse,Error>]()) { current,next in
current[next.0] = next.1
}
.share(replay: 1)
每个单元格都可以订阅上述内容,并使用compactMap
提取其特定状态。