我有一个表,它正在从我的视图模型中的shiftCells
数组填充。 渲染了 ~8 种不同类型的单元格,我需要根据单击的单元格对每个单元格执行不同的操作。 我试图找出在视图模型中处理此问题的最佳方法,以便视图控制器只关心最终结果(因此它可以导航到新的视图控制器等),但我正在努力找出一种正确组织它的方法。 以下是我当前的表视图单击绑定在 VC 中的样子:
shiftsTableViewController
.cellSelection
.subscribe(onNext: { [weak self] (cellType: ShiftSelectionsTableViewCellType) in
guard let sSelf = self else { return }
switch cellType {
case .cellTypeA(let viewModel):
self.performSomeAction(viewModel: viewModel)
...etc...
}
}).disposed(by: disposeBag)
根据单元格的类型,我执行自定义操作(它可能是网络请求,结果将进入导航到的新 VC,或者可能是其他内容)。 以下是performSomeAction
的外观:
func performSomeAction(viewModel: ShiftTypeCellViewModel) {
self.networkService
.perform(
shiftId: viewModel.shiftId, // notice i need data from cell vm
pin: self.viewModel.pin, // i also need data from root vm
photoURL: self.viewModel.imageURL)
.subscribe(onNext: { result in
self.navigateToPage(result: result)
}, onError: { error in
// show error banner
}).disposed(by: disposeBag)
}
因此,对于 ~8 种不同类型的单元格,我有 8 种方法,它们都做了一些不同的事情。 是否应将这些方法移动到 VM 中? 我是否应该将此方法移动到单元 VM 中并直接在单元视图中处理单击事件? 建议/一个例子将不胜感激。
根据您在问题中提供的内容,我认为我最终会得到以下结果:
在视图中的 VC 加载:
let result = viewModel.generateRequest(forCell: tableView.rx.modelSelected(ShiftSelectionsTableViewCellType.self).asObservable())
.flatMapLatest { [networkService] arg in
(networkService?.perform(shitId: arg.shiftId, pin: arg.pin, photoURL: arg.photoURL) ?? .empty())
.materialize()
}
.share(replay: 1)
result.compactMap { $0.element }
.bind(onNext: { [weak self] result in
self?.navigateToPage(result: result)
})
.disposed(by: disposeBag)
result.compactMap { $0.error }
.bind(onNext: { error in
// show error banner
})
.disposed(by: disposeBag)
在视图中模型:
func generateRequest(forCell cell: Observable<ShiftSelectionsTableViewCellType>) -> Observable<(shiftId: String, pin: String, photoURL: URL)> {
return cell.map { [pin, imageURL] cellType in
switch cellType {
case .cellTypeA(let viewModel):
return (shiftId: viewModel.shiftId, pin: pin, photoURL: imageURL)
}
// etc...
}
}
这样,所有副作用都在 VC 中,所有逻辑都在 VM 中。上面将networkService
放在 VC 中,因为它是一个副作用对象。
您的视图控制器应仅处理具有单元格选择的 UI 然后,您的视图模型应该是数据的促进者。此外,如果可能的话,我会尽量避免您的单元格具有 UI 代码以外的任何内容。
// ViewController
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
viewModel.performSomeAction(indexPath: indexPath)
}
func setupObservables() {
viewModel.outputs.reloadRelay
.subscribeOn(MainScheduler.instance)
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] _ in
guard let uwSelf = self else { return }
uwSelf.tableView.reloadData()
}).disposed(by: disposeBag)
}
// ViewModel
// This BehaviorRelay is kinda silly but i'm and trying to demonstrate a trigger that your viewcontroller can listen for to update your UI
var reloadRelay: BehaviorRelay<Bool> = BehaviorRelay(value: false)
func performSomeAction(indexPath: IndexPath) {
switch (indexPath.section, indexPath.row) {
case (1, _):
// Make a network call then update your data
service.randomCall(id: id)
.subscribeOn(MainScheduler.instance)
.subscribe(onNext: { result in
// Update data with result response data
self.navigateToPage(result: result)
}, onError: { [weak self] error in
print(error)
}).disposed(by: disposeBag)
case (2, _):
// Update your data then reload the table
reloadRelay.accept(true)
case (3, _):
// Do something else
case (4, 0):
// Do something else
...
default: break
})
}