我正在参与「启航计划」

接着上一篇 Swift中MVVM关于列表中Cell的拆分(上) 持续剖析余下的

1. ViewController

我之前运用的是对RxTableview的绑定,但是实际情况咱们考虑数据itemts的动态改变,比方在某些功能在登录下和未登录下items是不一样的。或许权限不同展现不同的cell,因而实际上cell是灵活的最好的,虽然我这儿是写死的。

Swift中MVVM对于列表中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

咱们持续看下ViewModletransform的实现,咱们看下inputoutput的界说

Swift中MVVM对于列表中Cell的拆分(下)

咱们要处理请求来决议咱们的item,或许展现

Swift中MVVM对于列表中Cell的拆分(下)

咱们把触发的序列合并为一个新的序列,方便咱们订阅,这儿我是把input中的viewWillAppear和用户信息,版别信息请求合并为一个新的序列进行订阅

Swift中MVVM对于列表中Cell的拆分(下)

处理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不同,当咱们版别更新请求没有结果的时分

Swift中MVVM对于列表中Cell的拆分(下)

请求结果后,数据bind的列表也改写了

Swift中MVVM对于列表中Cell的拆分(下)

当咱们版别请求结果出来后发送结果,改变了数据源,然后改写了绑定的列表

Swift中MVVM对于列表中Cell的拆分(下)

一些咱们不用跳转的逻辑,比方打电话或许打开系统url,咱们可以直接订阅处理

Swift中MVVM对于列表中Cell的拆分(下)

3. 总结

关于咱们一个大的列表中咱们可以区别把cell的一些行为绑定到cell的ViewModel中处理,然后拆分一部分咱们的逻辑处理,这样咱们cell的行为就由cell的ViewModel本身来维护。本质上来说是划分颗粒度,拆分咱们控制器中ViewModel的逻辑

这儿参考的 SwiftHub项目