RxSwift(14)—— MVVM双向绑定

RxSwift 篇章已经接近尾声,回首这个历程很累但很充实:白天备课,晚上VIP上课!忙里偷闲写下了这个篇章。可以说:痛并快乐着

当我列出下面的列表的时候,突然发现好幸福,每一个篇章,都是自己坚持的成功,都是自己努力的收获,感谢自己每个凌晨的灵感,感谢陪我一起前行的我的周边人

就问此时此刻还有谁?45度仰望天空,该死!我这无处安放的魅力!


RxSwift目录直通车--- 和谐学习,不急不躁!


一、首先面向开发我们看看RxSwift在TableView的应用

/// tableView -- RX
func setupTableViewRX() {
    let dataOB = BehaviorSubject.init(value: self.viewModel.dataArray)
    // 骚起来
//        dataOB.bind(to: self.tableView.rx.items){(tabView,row,model) ->LGTableViewCell in
//            let cell = tabView.dequeueReusableCell(withIdentifier: resuseID) as! LGTableViewCell
//            cell.setUIData(model as! LGModel)
//            return cell
//        }.disposed(by: disposeBag)
    
    dataOB.asObserver().bind(to: self.tableView.rx.items(cellIdentifier: resuseID, cellType: LGTableViewCell.self)){
        (row,model,cell) in
        cell.setUIData(model as! LGModel)
    }.disposed(by: self.disposeBag)
    
    // tableView点击事件
    tableView.rx.itemSelected.subscribe(onNext: { [weak self](indexPath) in
        print("点击\(indexPath)行")
        self?.navigationController?.pushViewController(LGSectionViewController(), animated: true)
        self?.tableView.deselectRow(at: indexPath, animated: true)
    }).disposed(by: self.disposeBag)
    
    // tableView复选点击事件
    tableView.rx.itemDeselected.subscribe(onNext: { (indexPath) in
        print("再次点击\(indexPath)行")
    }).disposed(by: self.disposeBag)
    
    // tableView移动事件
    tableView.rx.itemMoved.subscribe(onNext: { [weak self] (sourceIndex,destinationIndex) in
        print("从\(sourceIndex)移动到\(destinationIndex)")
        self?.viewModel.dataArray.swapAt(sourceIndex.row, destinationIndex.row)
        self?.loadUI(obSubject: dataOB)
    }).disposed(by: self.disposeBag)
    
    // tableView删除事件
    tableView.rx.itemDeleted.subscribe(onNext: { [weak self](indexPath) in
        print("点击删除\(indexPath)行")
        self?.viewModel.dataArray.remove(at: indexPath.row)
        self?.loadUI(obSubject: dataOB)
    }).disposed(by: self.disposeBag)
    
    // tableView新增事件
    tableView.rx.itemInserted.subscribe(onNext: { [weak self](indexPath) in
        print("添加数据在\(indexPath)行")
        guard let model = self?.viewModel.dataArray.last else{
            print("数据有问题,无法新增")
            return
        }
        self?.viewModel.dataArray.insert(model, at: indexPath.row)
        self?.loadUI(obSubject: dataOB)
    }).disposed(by: self.disposeBag)    
}
  • TableView点击、复选、新增、删除、移动全部简洁实现
  • RxSwift一旦遇上了TableView,根本不需要那些恶心的胶水代码
  • delegate & dataSource的赋值直接免去
  • 胶水代理以及数据源代理的实现(毕竟我们现在列表界面越来越多,胶水的代码的优化,太爽了)
  • 我们所有的事件订阅都在相应地方,我们更容易管理
  • 函数式回调导致我们的逻辑代码与功能代码绑定在一块,可读性更强

二、分组的TableView

/// Rx 处理分组
func setupTableViewRX() {
    let tableViewDataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,LGSectionModel>>(configureCell: { [weak self](dataSource, tab, indexPath, model) -> LGTableViewCell in
        let cell = tab.dequeueReusableCell(withIdentifier: self?.resuseID ?? "resuseID_LGSectionViewController", for: indexPath) as! LGTableViewCell
        cell.setUISectionData(model)
        cell.selectionStyle = .none
        return cell
    },titleForHeaderInSection: { dataSource,index -> String in
        // print("数据:\(dataSource.sectionModels[index].identity)")
        return dataSource.sectionModels[index].model
    })

    /// 我们上次就是通过bind函数,这里我们变化一下
    self.data.githubData.asDriver(onErrorJustReturn: [])
        .drive(self.tableView.rx.items(dataSource: tableViewDataSource))
        .disposed(by: self.disposeBag)
    
    /// 跳转到网络相关Demo
    self.tableView.rx.itemSelected.subscribe(onNext: { [weak self](indexPath) in
        self?.navigationController?.pushViewController(LGNetworkViewController(), animated: true)
    }).disposed(by: self.disposeBag)
}
  • RxCocoa封装的dataSourcer让我们爽的无法自拔
  • dataSource的承担了整个数据源代理,并且强势封装了代理
  • 数据层的响应直接绑定到了UI,让数据更简单传输
  • 从上面的代码一眼就能看出,RxSwift让我们的开发更直接面向开发,让开发更容易

MVVM双向绑定

override func viewDidLoad() {
    super.viewDidLoad()
    self.setupUI()
    // 现在来一个需求:我们的输入框搜索 - 请求网络 - 下面tableView联动
    // UI <-> model
    self.searchBar.rx.text.orEmpty
        .bind(to: self.viewModel.searchTextOB)
        .disposed(by: disposeBag)
    
   // 数据层绑定UI
    self.viewModel.searchData.drive(self.tableView.rx.items){ (tableView, indexPath, model) -> LGTableViewCell in
        let cell = tableView.dequeueReusableCell(withIdentifier: self.resuseID) as! LGTableViewCell
        cell.nameLabel.text  = model.name
        cell.classLabel.text = model.url
        return cell
    }
    .disposed(by: disposeBag)
    
    // tittle绑定
    self.viewModel.searchData.map { (array) -> String in
        return "搜索到了 \(array.count) 条数据"
    }
    .drive(self.navigationItem.rx.title)
    .disposed(by: disposeBag)
    
    // 滑动减速绑定
    self.tableView.rx.didEndDecelerating
        .subscribe { [weak self] in
            self?.searchBar.endEditing(true)
    }.disposed(by: disposeBag)
}
  • 这里就是我们RxSwift世界里的ViewController,这才是真正的轻量化的VC,至始至终都只做一件事:建立View与ViewModel之间的绑定依赖关系
  • 通过我们搜索栏的响应,发送数据给我们的ViewModel,让它在它的世界里处理业务层,网络层数据的返回
  • 通过ViewModel响应对外,达到数据绑定到UI的效果
  • ViewModel里面我们运用RxSwift的高阶函数,处理逻辑
lazy var searchData: Driver<[LGReposityModel]> = {
    return self.searchTextOB.asObserver()
        .throttle(RxTimeInterval.milliseconds(300), scheduler: MainScheduler.instance)
        .distinctUntilChanged()
        .flatMapFirst(LGComprehensiveViewModel.resposeData)
        .asDriver(onErrorJustReturn: [])
}()
  • UI事件搜索的响应传入,通过一个懒加载的数据返回
  • 这个响应的序列通过throttle保证了相隔0.3秒发送一次事件
  • distinctUntilChanged函数保证了带宽,直到搜索字眼变动的时候才请求
  • flatMapFirst因为序列的序列,我们下沉请求,回调结果
  • asDriver包装成Drive序列,保证状态共享,主线程调度,没有错误返回
static func resposeData(_ githunId: String) -> Observable<[LGReposityModel]> {
    guard !githunId.isEmpty,let url = URL(string: "https://api.github.com/users/\(githunId)/repos") else {
        return Observable.just([])
    }
    
   return URLSession.shared.rx.json(url: url)
            .retry()
            .observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
            .map(LGComprehensiveViewModel.parseData)
}
  • 通过上面的响应,这里封装网络URLSession.shared.rx.json,返回json
  • 为了保证请求网络在子线程,这里调用observeOn
  • 请求回来的数据还不是我们所想要的,这里map映射,下沉的序列化结果
static func parseData(_ json: Any) -> [LGReposityModel] {
    guard let items = json as? [[String: Any]] else {return []}
    guard let result = [LGReposityModel].deserialize(from: items) else { return [] }
    return result as! [LGReposityModel]
}
  • 这里就是普通的判空,然后通过HandJSON进行数据解析
  • 返回的结果序列成模型数组,然后返回响应
  • 响应层层下发,数据层层处理上升
  • 根据外界的关系,完美实现双向绑定

完美实现联动,MVVM双向绑定加入RxSwift的身影也就更出色

  • 当北京遇上西雅图
  • 当爱情遇上婚姻
  • 当兴趣遇上事业
  • 当MVVM遇上RxSwift
  • 我的天一切的一切都是那么的美好,安逸~~~舒服!!!

所有的努力都会在某一个时刻兑现,我们很多小伙伴都会感叹怎么还不来!你要知道:毋庸置疑,好的事情总会到来,当他来晚的时候也不失为一种惊喜

就问此时此刻还有谁?45度仰望天空,该死!我这无处安放的魅力!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容