RxSwift入坑好多天-终于有了一点理解

一、前言

  • 江湖上都在说现在就要赶紧学 swift 了,即将是 swift 的天下了。在 api 变化不大的情况下,swift 作为一门新的语言,集众家之所长,普通编码确实比 oc 要好用的多了
  • 老早就听说 MVVM 的概念及响应式函数式编程,微软确实厉害。自己最近没什么事,就前来入坑了

二、学习方式

三、自己写注册登录及 tableView 的一点理解

  • 关于观察者被观察者(Observable)发出序列
    • UI 控件在 RxCocoa 下某些属性都是被观察者(Observable),都可以发出序列,常见的有
      • 控件的 text 类型是 ControlProperty<String> ,最终遵循 ObservableType协议
      • 按钮的点击 tap 类型是 ControlEvent<Void>,最终遵循 ObservableType协议
    • 对于设置 UI 控件的一些 Bool 类型的属性,如可输入,可点击,一般用 UIBindingObserver<UIElementType, Value>(遵循 ObserverType协议) 来生成观察者,对接受的数据条件进行判断是否可以输入、可点击

      // MARK: RX 扩展 计算型属性
      // textfield 根据展示验证后的结果能否输入,验证过了才能输入
      extension Reactive where Base: UITextField {
      
       var inputEnable: UIBindingObserver<Base, ValidationResult> {
          return UIBindingObserver(UIElement: base, binding: { 
              (textField, result) in
      
              textField.isEnabled = result.isValid
           })
       }
      }
    • 关于在 VM 中常用的 Subject
      • Variable、PublishSubject 是 Subject 的一种,可当观察者被 bindTo,可当序列数据源 Observable
        • Variable 它不会因为错误终止也不会正常终止, 适合做数据源,可以用于控件的 text 属性
        • PublishSubject 与普通的Subject不同,在订阅时并不立即触发订阅事件,而是允许我们在任意时刻手动调用onNext(),onError(),onCompleted来触发事件,可以用于按钮的点击
    • 关于被观察者(Observable)的一些常用的 api
      • map 不会产生新的序列
      • flatMapLatest 会产生新的序列
      • combineLatest 不会产生新的序列
  • 关于 MVVM 文件夹分类
    • 之前 MVC 的与 iOS 里的 Controller、View 一一对应,很好理解,而 MVVM 里 Controller 属于 V 了,负责处理控制器跳转和将 View 和 VM 绑定等,大部分的业务逻辑代码都在 VM 里,感觉应该是这样
      RxSwift入坑好多天-终于有了一点理解

    • 自始至终感觉 iOS 里的 model 这一层很轻,有时仅仅是建立了模型类而已。感觉应该是各种数据操作如数据库查询等都应该是 model 这一层的

  • 关于** 双向绑定 **
    • 首先要有一些控件,理清楚需要监听这些控件的哪些属性值
    • 然后 VM 里建立好这些属性值对应的 Subject
    • 一般控制器里生成 VM 对象,将控件的 Observable 的属性绑定到 VM 的 Subject 属性上,这样可在 VM 里监听到控件属性值的改变,此时 Subject 是 Observer,完成一次绑定
    • 在 VM 内,将 Subject 变成 Observable,生成对应 VM 可被观察者属性(用属性保存加工变换后的 Observable )。这里 Subject 是 Observable,可通过 map 、filter 等各种操作,操作的数据就是 Subject 观察到的序列,相应模块的整个业务逻辑都在此处。
    • 在控制器里,再将 VM 的可被观察者属性绑定到 UI 控件上,在此完成双向绑定
    override func viewDidLoad() {
        super.viewDidLoad()
        let regiestViewModel = RegiestViewModel()
        // 这里做绑定: UI控件 --> VM    VM -> UI控件
        // 1.UI控件 --> VM
        nameTextField.rx.text.orEmpty
            .bindTo(regiestViewModel.username)
            .addDisposableTo(disposeBag)
        pwdTextField.rx.text.orEmpty
            .bindTo(regiestViewModel.userPwd)
            .addDisposableTo(disposeBag)
        repeatPwdTextField.rx.text.orEmpty
            .bindTo(regiestViewModel.repeatPwd)
            .addDisposableTo(disposeBag)
        regiestBtn.rx.tap
            .bindTo(regiestViewModel.registerTaps)
            .addDisposableTo(disposeBag)
        // 2.VM -> UI控件
        // 显示结果的 label 上
        regiestViewModel.usernameValid
            .bindTo(nameTipLabel.rx.validResult)
            .addDisposableTo(disposeBag)
        // 绑定 密码框是否可以输入
        regiestViewModel.usernameValid
            .bindTo(pwdTextField.rx.inputEnable)
            .addDisposableTo(disposeBag)
        regiestViewModel.passwordValid
            .bindTo(pwdTipLabel.rx.validResult)
            .addDisposableTo(disposeBag)
        regiestViewModel.passwordValid
            .bindTo(repeatPwdTextField.rx.inputEnable)
            .addDisposableTo(disposeBag)
        regiestViewModel.repeatPwdValid
            .bindTo(repeatPwdTipLabel.rx.validResult)
            .addDisposableTo(disposeBag)
        // 按钮不是绑定, 按钮是 subcribe, 需要操作的
        regiestViewModel.registerButtonEnabled
            .subscribe (onNext: { [weak self]  (result) in
                self?.regiestBtn.isEnabled = result
                self?.regiestBtn.alpha = result ? 1 : 0.8
                })
            .addDisposableTo(disposeBag)
        // 注册结果 : 注册成果或失败 要展示在 UI 上
        regiestViewModel.registeResult
            .subscribe(onNext:{ [weak self] result in
                switch result {
                    case let .failed(message):
                        self?.showAlter(message: message)
                    case let .ok(message):
                        self?.showAlter(message: message)
                    case .empty:
                        self?.showAlter(message: "")
                    }
                })
            .addDisposableTo(disposeBag)
        // 跳转到登录界面按钮的点击
        loginVcBtn.rx.tap
            .subscribe(onNext: {
                let loginVc = LoginViewController()
                loginVc.title = "请登录"
                self.navigationController?.pushViewController(loginVc, animated: true)
            })
            .addDisposableTo(disposeBag)
    }
    
  • 关于 tableView
    • 这里需要 RxDataSources 这个配套的框架
    • 控制器里需要一个 dataSource

      let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, HerosItem>>()
    • 控制器里用这个 dataSource 配置 cell

      dataSource.configureCell = { (_, tableView, indexPath, item) in
          var cell = tableView.dequeueReusableCell(withIdentifier: "herosCell")
          if cell == nil {
              cell = UITableViewCell(style: .subtitle, reuseIdentifier: "herosCell")
          }
          cell!.imageView?.image = UIImage(named: item.icon)
          cell!.textLabel?.text = item.name
          cell!.detailTextLabel?.text = item.intro
          return cell!
      }
      
    • 用 VM 创建出数据 Observable,发出序列,绑定到dataSource 上,完成数据的绑定

      homeViewMode.getSearchResult()
          .bindTo(tableView.rx.items(dataSource: dataSource))
          .addDisposableTo(disposeBag)
    • 其他操作
      • tableView 的代理

        tableView.rx
        .setDelegate(self)
        .addDisposableTo(disposeBag)
      • tableView cell 的点击

        tableView.rx.itemSelected
        .map { [weak self] indexPath in
            return (indexPath, self?.dataSource[indexPath])
        }
        .subscribe(onNext: {(indexPath, item) in
            self.showAlter(item: item)
        })
        .addDisposableTo(disposeBag)
    • 感觉像一个固定的代码模式,将数据源的代码都移到 VM 里了
    • 另外如果做实时搜索的话,用双向绑定效果那是极好的,将搜索框的搜索关键字绑定到 VM 里,在用 VM 产生序列绑定到 tableView 上

四、本例 demo 地址

五、其他

  • MVVM 的使用并没有那么普及,大多数还是 MVC,关于 MVC 的理解和减少控制器的代码量和维护难度是一个难题,有篇文章可参考 http://www.infoq.com/cn/articles/rethinking-mvc-mvvm
  • 对于 RxSwift,像 swift 里的可选类型的处理及 swift 里的多种闭包类型及简写,理解还急需提高;对 RxSwift 的 api 还要继续熟悉,以及后面的多线程及整个项目都用 MVVM 部署还需要更多的实践
更多相关文章
  • Android 终于要跟 Chromium 和 WebKit 团队合作了
    我觉得有必要给大家推荐一下,扫一下盲,我都一直以为Android使用的是chrome的浏览器.为啥Android里的浏览器跟Chrome没任何关系?这是很多安粉和克粉共同的问题,不得不说Android里的浏览器距离其它移动平台里的浏览器差距甚远,更别提桌面版的Chrome了.不过好在Google终于 ...
  • ENode2.0-深入分析ENode的内部实现流程和关键地方的幂等设计
    前言 ENode是一个基于消息的架构,使用ENode开发的系统,每个环节都是处理消息,处理完后产生新的消息.本篇文章我想详细分析一下ENode框架内部是如何实现整个消息处理流程的.为了更好的理解我后面的流程的描述,我觉得还是应该先把ENode的架构图贴出来,好让大家在看后面的分析时,可以对照这个架构 ...
  • 本文章重点给各位同学介绍关于Nginx+PHP(FastCGI)+MySQL在小内存VPS(t1.micro)上的安装配置与优化方法,文章记录了安装配置的整个过程希望对大家有帮助.背景介绍:以前,因为AWS EC2的价格比较昂贵而租用了其它国外小厂商的VPS,在使用了3年多之后,发现AWS EC2的 ...
  • 战舰少女6-1练cv及bbbc攻略分享
    在战舰少女的这一款游戏里面,6-1a已经接替3-4成为最佳练级点位,今天小编就来给各位玩家们来详细的说一下6-1a位置的详细情况,下面就和小编一起来看一下.给各位玩家们分享一下战舰少女6-1练cv及bbbc攻略的详细情况.攻略一览:先说下6-1a敌方配置.5ss3ss2航母3ss2雷巡在本版本(1. ...
  • 音乐是一种很美的艺术,让人禁不住就陶醉其中,不管是优美的还是激烈的,都有震撼人心的巨大效应.好的音乐永存于世间,能够跨越时间和空间.1. 太阳升起来了,把大地照得银光闪闪的,好像撒下了一颗颗珍珠.雪融化了,雪水流入了小溪.你看,溪水是那么的清澈.一群小鱼聚拢在一起,似乎双手一捧就能捧起很多鱼儿似的. ...
  • Android Studio 单刷第一行代码——Android系列
    本系列将使用 Android Studio 将<第一行代码——Android>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Android Studio 作为开发 IDE 的同学开路.关键词<第一行代码>:CSDN 知名博主郭霖所著,A ...
  • EC2上MySQL如何开启远程访问
    最近在亚马逊云主机上安装MySQL,想远程访问,结果无论如何都访问不了.在踩了若干坑之后,终于访问成功了,在此做一下记录:EC2上安装了MySQL后如何开启远程访问.一.遇到的问题想远程访问EC2上的MySQL,总提示: ERROR 2003 (HY000): Can't connect to My ...
  • 在Ubuntu下搭建Glut开发环境
    首先下载codeblocks,这是个不错的开源c/c++编辑器,而且支持多平台,强烈推荐哦-然后在软件包管理器中下载freeglut-dev开发包,下载完成后会自动安装到系统的/usr路径下面.安装完成后,/usr/include/下面会出现freeglut.h头文件,/usr/lib/下面会出现l ...
  • 提问 利用什么技术可以快速的搭建个人blog,博客源代码使用是markdown编写,也可以是静态的,初步使用github托管. 请有经验的帮忙分享一下使用到的工具,最好配一个简要的搭建流程! 这里写的很详细如何搭建一个独立博客--简明Github Pages与Hexo教程 搭建个人博客有很多办法,不 ...
一周排行