我正在参与「启航计划」
接着上一篇 Swift中MVVM关于列表中Cell的拆分(上) 持续剖析余下的
1. ViewController
我之前运用的是对RxTableview
的绑定,但是实际情况咱们考虑数据itemts
的动态改变,比方在某些功能在登录下和未登录下items是不一样的。或许权限不同展现不同的cell
,因而实际上cell是灵活
的最好的,虽然我这儿是写死的。
我这儿的写法还是依照之前开发经验,实际上并没有让viewModel来处理。首先关于一个tableview来说咱们一般要输入下面的情况:
1. items
是动态改变的
2. cell
是否可点击是应该在viewModel
中处理
所以咱们输入来说要一个触发条件
,比方下拉改写,viewWillAppear
等。关于输出,咱们输出要展现的items绑定到Tableview
,依据选择的cell,输出对应的SettingsSectionItem
。
struct Input {
/// 退出登录
var logoff: Driver<Void>
/// 改写信息
let trigger: Observable<Void>
/// 点击cell
let selection: Driver<SettingsSectionItem>
}
struct Output {
/// 加载状况
var loadingSigal: Driver<Bool>
/// 退出登录结果
var logoff: Driver<Bool>
/// itets
let items: BehaviorRelay<[SettingsSection]>
/// 点击cell结果
let selectedEvent: Driver<SettingsSectionItem>
}
咱们在绑定的办法中咱们界说改写的序列:
/// 改写触发
let refresh = rx.viewWillAppear.mapToVoid()
mapToVoid
这个是一个拓宽,转化为Void
类型序列
extension ObservableType {
func mapToVoid() -> Observable<Void> {
return map { _ in }
}
}
- 输入
let input = SettingViewModel.Input(logoff: self.settingView.logoutBtn.rx.tap.asDriver(), trigger: refresh, selection: settingView.tableView.rx.modelSelected(SettingsSectionItem.self).asDriver())
- tableview绑定
/// dataSource绑定tableview
let dataSource = RxTableViewSectionedReloadDataSource<SettingsSection>(configureCell: {
[weak self](dataSource, tab, indexPath, item) -> SettingCell in
let cell = self?.settingView.tableView.dequeueReusableCell(withIdentifier: SettingCell.description()) as! SettingCell
switch item {
case .userInfo(let viewModel),
.service(let viewModel),
.aboutPlatform(let viewModel),
.privacyPolicy(let viewModel):
cell.bind(to: viewModel)
return cell
case .update(viewModel: let viewModel, isUpdate: _ ,updateAddress: _):
cell.bind(to: viewModel)
return cell
}
})
依据不同的cell进行bind不同的viewModel
,这儿的viewModel
代替了咱们常用的model
。
- 绑定items
output.items.asObservable()
.bind(to:settingView.tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
- 绑定点击事情
/// 页面跳转
output.selectedEvent.drive(onNext: { [weak self] (item) in
var controller: UIViewController? = nil
switch item {
case .userInfo:
controller = self?.navigator.get(segue: NavigatorMe.Scene.info)
case .aboutPlatform(let viewModel),
.privacyPolicy(let viewModel):
controller = WKWebViewController.init(url: MeAPI.webURL.webLinkURL(hospitalCode: ResourceSingle.shared.hospitalCode, smsCode: viewModel.code))
controller?.title = viewModel.title.value
default:break
}
if let nextController = controller {
self?.navigationController?.pushViewController(nextController, animated: true)
}
}).disposed(by: disposeBag)
针对不同事情进行跳转,一般咱们在controller
中做一些绑定和路由跳转的实现。
- 一些自界说需求输出订阅
/// loadng框显示
output.loadingSigal
.drive { self.view.isLoading(isStop:$0) }
.disposed(by: disposeBag)
/// 退出登录
output.logoff.filter{$0}.drive(onNext: { _ in
UserManager.shared.cleanData()
BYNotificationCenter.shared.postNotificationLoginOut()
}).disposed(by: disposeBag)
在mmvm结构中,咱们在控制器主要是进行绑定关系
以及路由跳转
。
2. ViewModel
咱们持续看下ViewModle
的transform
的实现,咱们看下input
和output
的界说
咱们要处理请求来决议咱们的item
,或许展现
咱们把触发的序列合并为一个新的序列,方便咱们订阅,这儿我是把input
中的viewWillAppear
和用户信息,版别信息请求合并为一个新的序列进行订阅
处理items逻辑
refresh.map {(_) -> [SettingsSection] in
/// 处理items的逻辑,比方登录与否等,这儿没有直接初始化
var items: [SettingsSection] = []
items += [
SettingsSection.setting(title: "", items: [SettingsSectionItem.userInfo(viewModel: SettingBadgeCellViewModel(with: "个人信息", detail: "基础信息", image: UIImage.getBundleImage(imageName:"me_settting_info"), hidesDisclosure: false, isUpdate: false))]),
SettingsSection.setting(title: "", items: [SettingsSectionItem.service(viewModel: SettingBadgeCellViewModel(with: "联系客服", detail: R.key.resource.phone, image: UIImage.getBundleImage(imageName:"me_settting_services"), hidesDisclosure: true, isUpdate: false,highlightColor: R.color.color10AA89))]),
SettingsSection.setting(title: "", items: [
SettingsSectionItem.aboutPlatform(viewModel: SettingBadgeCellViewModel(with: "关于渠道", detail: "查看", image: UIImage.getBundleImage(imageName:"me_settting_info"), hidesDisclosure: false, isUpdate: false,code: "gywm")),
SettingsSectionItem.privacyPolicy(viewModel: SettingBadgeCellViewModel(with: "法令声明与隐私方针", detail: "查看", image: UIImage.getBundleImage(imageName:"me_settting_law"), hidesDisclosure: false, isUpdate: false,code: "flsmyszc"))]),
]
if let update = self.isNeedUpdate.value {
let updateViewModel = SettingsSectionItem.update(viewModel: SettingBadgeCellViewModel(with: "检查更新", detail: "", image: UIImage.getBundleImage(imageName:"me_settting_update"), hidesDisclosure: true, isUpdate: true,isNewest: self.isNeedUpdate.value),isUpdate: update, updateAddress: self.updateAddress.value)
let updateSection = SettingsSection.setting(title: "", items: [updateViewModel])
items.append(updateSection)
}
return items
}.bind(to: elements).disposed(by: cellDisposeBag)
这儿跟着咱们条件序列不同,产生的items不同
,当咱们版别更新请求没有结果的时分
请求结果后,数据bind
的列表也改写了
当咱们版别请求结果出来后发送结果,改变了数据源,然后改写了绑定的列表
一些咱们不用跳转的逻辑,比方打电话或许打开系统url,咱们可以直接订阅处理
3. 总结
关于咱们一个大的列表中咱们可以区别把cell
的一些行为绑定到cell的ViewModel中处理
,然后拆分
一部分咱们的逻辑处理,这样咱们cell的行为就由cell的ViewModel
本身来维护。本质上来说是划分颗粒度,拆分咱们控制器中ViewModel的逻辑
。
这儿参考的 SwiftHub项目