RxSwift 入坑好多天 - 终于有了一点理解
一、前言
- 江湖上都在说现在就要赶紧学 swift 了,即将是 swift 的天下了。在 api 变化不大的情况下,swift 作为一门新的语言,集众家之所长,普通编码确实比 oc 要好用的多了
- 老早就听说 MVVM 的概念及响应式函数式编程,微软确实厉害。自己最近没什么事,就前来入坑了
二、学习方式
- 参考别人写的一些博客,对于概念先有个理解,然后参考官方 example,就可以开始学习了
- 推荐文章:
http://www.codertian.com/2016/11/27/RxSwift-ru-keng-ji-read-document/
http://www.codertian.com/2016/12/10/RxSwift-shi-zhan-jie-du-base-demo/
三、自己写注册登录及 tableView 的一点理解
关于
观察者
和被观察者(Observable)发出序列
- UI 控件在 RxCocoa 下某些属性都是被观察者(Observable),都可以发出序列,常见的有
- 控件的 text 类型是
ControlProperty<String>
,最终遵循ObservableType
协议 - 按钮的点击 tap 类型是
ControlEvent<Void>
,最终遵循ObservableType
协议
- 控件的 text 类型是
- 对于设置 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来触发事件,可以用于按钮的点击
- Variable、PublishSubject 是 Subject 的一种,可当观察者被 bindTo,可当序列数据源 Observable
- 关于
被观察者(Observable)
的一些常用的 api- map 不会产生新的序列
- flatMapLatest 会产生新的序列
- combineLatest 不会产生新的序列
- UI 控件在 RxCocoa 下某些属性都是被观察者(Observable),都可以发出序列,常见的有
关于 MVVM 文件夹分类
之前 MVC 的与 iOS 里的 Controller、View 一一对应,很好理解,而 MVVM 里 Controller 属于 V 了,负责处理控制器跳转和将 View 和 VM 绑定等,大部分的业务逻辑代码都在 VM 里,感觉应该是这样
自始至终感觉 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)
- tableView 的代理
感觉像一个固定的代码模式,将数据源的代码都移到 VM 里了
另外如果做实时搜索的话,用双向绑定效果那是极好的,将搜索框的搜索关键字绑定到 VM 里,在用 VM 产生序列绑定到 tableView 上
四、本例 demo 地址
五、其他
- MVVM 的使用并没有那么普及,大多数还是 MVC,关于 MVC 的理解和减少控制器的代码量和维护难度是一个难题,有篇文章可参考 http://www.infoq.com/cn/articles/rethinking-mvc-mvvm
- 对于 RxSwift,像 swift 里的可选类型的处理及 swift 里的多种闭包类型及简写,理解还急需提高;对 RxSwift 的 api 还要继续熟悉,以及后面的多线程及整个项目都用 MVVM 部署还需要更多的实践
RxSwift 入坑好多天 - 终于有了一点理解的更多相关文章
- oracle入坑日记<三>用户详解(角色理解)
1 用户是什么 1.1.权限管理是Oracle的精华,不同用户登录到同一数据库中,可能看到不同数量的表,拥有不同的权限.Oracle 的权限分为系统权限和数据对象权限,共一百多种.如果把Oracl ...
- 大神都在看的RxSwift 的完全入坑手册
大神都在看的RxSwift 的完全入坑手册 2015-09-24 18:25 CallMeWhy callmewhy 字号:T | T 我主要是通过项目里的 Rx.playground 进行学习和了解 ...
- 【Xbox one S】开箱&开机&初入坑心得
再来一发水贴,先上产品标准照镇贴: 前言 身为一个资深单机游戏玩家,常年混迹在PC平台,但内心深处一直对主机有种迷之向往,感觉那才是单机游戏的正处之地,坐沙发上拿着手柄对着电视跌宕起伏才是正确的游戏姿 ...
- 入坑IT十年(二)技术以外
上一篇博客里提到:技术越来越简单,发布后不久,就看到<技术并不是越来越简单>,这显然是打擂台来了. 技术究竟是不是越来越简单?其实这个问题,要看你究竟是以什么角度来思考这个问题.我们可以举 ...
- Linux探索之路1---CentOS入坑笔记整理
前言 上次跟运维去行方安装行内环境,发现linux命令还是不是很熟练.特别是用户权限分配以及vi下的快捷操作.于是决定在本地安装一个CentOS虚拟机,后面有时间就每天学习一点Linux常用命令. 作 ...
- 1-STM32带你入坑系列(STM32介绍)
由于自己的物联网开发板上的单片机是用的STM32,但是有些朋友没有用过,所以我将用这块开发板,带着大家入门STM32 先介绍一下STM32,我是在大三下学期的时候开始接触STM32,当时是想做一个小车 ...
- c#调用c++ dll 入坑记录
1.DLL引用坑 [DllImport("NetDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConve ...
- web前端入坑第二篇:web前端到底怎么学?干货资料! 【转】
http://blog.csdn.net/xllily_11/article/details/52145172 版权声明:本文为博主[小北]原创文章,如要转载请评论回复.个人前端公众号:前端你别闹,J ...
- Linux运维一定要知道的六类好习惯和23个教训,避免入坑!
Linux运维一定要知道的六类好习惯和23个教训,避免入坑! 从事运维三年半,遇到过各式各样的问题,数据丢失,网站挂马,误删数据库文件,黑客攻击等各类问题. 今天简单整理一下,分享给各位小伙伴. 一. ...
随机推荐
- PosixIO
1.打开文件 int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t m ...
- [ Android 五种数据存储方式之四 ] —— ContentProvider存储数据
Android这个系统和其他的操作系统还不太一样,我们需要记住的是,数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据.那这个时候有读者就会提出问题,难道两个 ...
- oracle存储过程中文乱码问题
设置环境变量,新建变量,设置变量名:NLS_LANG,变量值:SIMPLIFIED CHINESE_CHINA.ZHS16GBK word哥,还是不行呀: 参考:http://idata.blog.5 ...
- 腾讯面试题:10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。
腾讯面试题:10G 个整数,乱序排列,要求找出中位数.内存限制为 2G. 题目和基本思路都来源网上,本人加以整理. 题目:在一个文件中有 10G 个整数,乱序排列,要求找出中位数.内存限制为 2G.只 ...
- Select与Epoll比较
一.问题引出 联系区别 问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一个描述符上面,另外的描述符虽然有数据但是不能读出来,这样实时性不能满足要求,大概的解 ...
- doubango(6)--Doubango协议栈中对RTP的管理
相关数据结构 1. tsip_dialog_invite_t 描述: 一个invite_dialog代表了一个invite期间的所有的信令流程,因此,它首先是一个普遍的dialog的特殊化结构, ...
- CAGradientLayer颜色渐变器
使用CAGradientLayer可以实现颜色的渐变, 我们先看下头文件 @interface CAGradientLayer : CALayer @property(nullable, copy) ...
- ASM实现Android APK的AOP日志统计
先通过ppt了解下ASM和AOP,然后通过github上的一个仓库代码看一下demo. 下面来看demo,这个demo完成了对目标类的方法注入执行时间统计的代码,在github:https://git ...
- localStorage的黑科技-js和css缓存机制
一.发现黑科技的起因 今天在微信公众号看到一篇技术博文,想用印象笔记收藏,所以发送了文章链接到pc上.然后习惯性地打开控制台,看看源码,想了解下最近微信用了什么新技术. 呵呵,以下勾起了我侦探的欲 ...
- eclipse安装git插件
用Eclipse开发,有时需要团队协作,git就是个比较好的选择.下面简单介绍一下git插件的安装方法: 1.Help -- install new software 打开插件安装界面 2.点ad ...