RxSwift系列
①、RxSwift基础控件的使用、RxSwif-Tableview的使用、RxSwift-SectionTableview结合RxDataSources的使用、RxSwift 网络请求封装的使用
②、RxSwift函数式响应编程思想,RxSwift-KVO、Button、UITextField、ScrollView、手势、通知、定时器、网络请求的使用
③、iOS-RxSwift核心逻辑,RxSwift实战案例,Observable的继承链,Observable订阅的流程、OnNext的流程、AnonymousObservableSink业务逻辑用
④、RxSwift的Timer、Observable的创建、RxSwiftUI控件(UIDatePicker、UIButton、UISwitch、UISlider、UIStepper)在实际开发的使用
推荐网站
RxSwift官方文档
RxSwift-Github源码
RxSwift的操作符Demo
Demo-①、RxSwift基础控件的使用、RxSwif-Tableview的使用、RxSwift-SectionTableview结合RxDataSources的使用、RxSwift 网络请求封装的使用
Demo-②、RxSwift函数式响应编程思想,RxSwift-KVO、Button、UITextField、ScrollView、手势、通知、定时器、网络请求的使用-1
Demo-②、RxSwift函数式响应编程思想,RxSwift-KVO、Button、UITextField、ScrollView、手势、通知、定时器、网络请求的使用-2-Observable
RxSwift实际案例
①、RxSwift的Timer
为什么不受RunLoop
影响、系统的Timer
有哪些受影响
0.如何鉴别Timer
是否受RunLoop
的影响
在页面添加一个TextView
开启定时器
看定时是否还在走动。
如果在走动 说明不受影响
1、Timer
- RunLoop
是.common
不受影响、.default
就受影响
从下面的代码看出 TImer加到RunLoop的Common里面去。不受印象
所以联想到 RxSwift timer - 猜测Timer -> .common
func code_timer(){
// timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
// RunLoop.current.add(timer, forMode: .default) // 受影响
// RunLoop.current.add(timer, forMode: .common) // 不受影响
// 受影响
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in
print("timer \(timer)")
})
}
@objc func timerFire(){
print("yh")
}
2、CGD
(定时器推荐使用,更精准)
func code_gcdTimer(){
// gcd 不受RunLoop影响
gcdTimer = DispatchSource.makeTimerSource()
gcdTimer?.schedule(deadline: DispatchTime.now(),repeating: DispatchTimeInterval.seconds(1)) // 开始
// 处理事务
gcdTimer?.setEventHandler(handler: {
print("code_gcdTimer hello GCD")
})
// 默认是挂起状态
// 重新开始、
gcdTimer?.resume()
// 内存管理问题 先要cancel 才能致空
// gcdTimer?.suspend()
// gcdTimer?.cancel()
// gcdTimer = nil
}
3、CADisplayLink
- RunLoop
是.common
不受影响
func code_CADisplayLink(){
// 受影响
cadTimer = CADisplayLink(target: self, selector: #selector(timerFire))
cadTimer?.preferredFramesPerSecond = 1 // 每秒帧数
cadTimer?.add(to: RunLoop.current, forMode: .default)
}
⭐️4、RxSwift的Timer是封装的GCD
我们先定义一个RxSwift的Timer
Observable<Int>.timer(RxTimeInterval.seconds(1), period: RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
我们查看该函数的内部 继续进入return Timer( dueTime: dueTime, period: period, scheduler: scheduler )
函数
找到Timer的run
函数.查看TimerSink
的类 找到run
函数 继续进去return self.parent.scheduler.schedulePeriodic(0 as Observer.Element, startAfter: self.parent.dueTime, period: self.parent.period!) { state in self.lock.performLocked { self.forwardOn(.next(state)) return state &+ 1 } }
找到ConcurrentDispatchQueueScheduler
的schedulePeriodic
,继续跟踪进去
找到DispatchQueueConfiguration
的schedulePeriodic
Observable<Int>.timer(RxTimeInterval.seconds(1), period: RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
> 最终定位到 DispatchQueueConfiguration的schedulePeriodic
看到代码
let timer = DispatchSource.makeTimerSource(queue: self.queue)
timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)
就是用GCD封装的
timerState = action(timerState)
我们希望timerState 是一个函数
我们回看代码 Timer的Run有一个尾随闭包
func run() -> Disposable {
return self.parent.scheduler.schedulePeriodic(0 as Observer.Element, startAfter: self.parent.dueTime, period: self.parent.period!) { state in
self.lock.performLocked {
self.forwardOn(.next(state))
return state &+ 1
}
}
}
//尾随闭包
{ state in
self.lock.performLocked {
self.forwardOn(.next(state))
return state &+ 1
}
forwardOn跟进去 最终会走 observer.on
self.observer.on(event)
1.Rxswift的Timer如何销毁
var timerDisposeBag = DisposeBag()
var RxTimer: Observable<Int>!
func Observable_Timer(){
// timer = Observable<Int>.timer(RxTimeInterval.seconds(1), period: RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
var a = 0
RxTimer = Observable<Int>.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
RxTimer.subscribe(onNext:{(num) in
a += 1
if a > 5 {
}
print(num)
}).disposed(by: timerDisposeBag)
// timer 序列 不发生 完成 & 错误 & 销毁
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("来了")
// 销毁RxSwift定时器1
timerDisposeBag = DisposeBag()
}
②、RxSwift-Observable
的创建
1.Observable
的empty
//MARK:observable 创建
// 空序列 - 本来序列的事件是Int类型,这里调用了empty函数 没有序列 。只能complete
func eobservable_empty(){
let emptyOb = Observable<Int>.empty()
_ = emptyOb.subscribe(onNext: {(number) in
print("eobservable_empty 订阅 :",number)
},onError: { (error) in
print("eobservable_empty error :",error)
}, onCompleted: {
print("eobservable_empty 完成")
},onDisposed: {
print("eobservable_empty 销毁 释放")
})
}
2.Observable
的just
func observable_just(){
// 数组
let array = ["yy","yh"]
Observable<[String]>.just(array)
.subscribe({(event) in
print(event)
})
.disposed(by: disposeBag)
}
3.Observable
的of
func observable_of(){
// 字典
let dict :[String : Any] = ["name":"yy","age":18]
Observable<[String: Any]>.of(dict)
.subscribe({(event) in
print(event)
})
.disposed(by: disposeBag)
}
4.Observable
的from
func eobservable_from(){
// MARK :from
// 从集合中获取序列 : 数组、结合、set - 获取序列 - 有可选项处理 -更安全
Observable<[String]>.from(optional: ["yy","yh"])
.subscribe({(event) in
print(event)
})
.disposed(by: disposeBag)
}
5.Observable
的defer
func observable_defer(){
// MARK :defer
// 这里有一个需求 : 动态序列 - 根据外界的标识 - 动态输出
// 使用deferred
// ()方法延迟 Observable 序列的初始化 通过传入的block来实现Observable序列的初始化并且返回。
var isodd = true
_ = Observable<Int>.deferred{() -> Observable<Int> in
// 这里设置我们的序列 -- 登录模块 username password 登录 注册
isodd = !isodd
if isodd {
return Observable.of(1,3,5,7,9)
}
return Observable.of(2,4,6,8)
}.subscribe{ (event) in
print(event)
}
}
6.Observable
的generate
func observable_genrate(){
// MARK :genrate
// 该方法创建一个只有当提供的所有判断条件都为 true的时候 。才会给出 Observable 序列
// 初始值给定 然后判断条件1 在判断条件2 会一直递归下去。直到条件1或 跳转2 不满足
// 类似 数组遍历循环
Observable.generate(initialState: 0,
condition: {$0 < 10},
iterate: {$0 + 2})
.subscribe({(event) in
print("eobservable_genrate ",event)
})
.disposed(by: disposeBag)
}
7.Observable
的timer
7.1 timer
func observable_timer(){
// MARK: timer
// 第一个参数 : 第一次响应距离现在的时间
// 第二个参数 : 时间间隔
// 第三个参数 : 线程
Observable<Int>.timer(RxTimeInterval.seconds(5), period: RxTimeInterval.seconds(2), scheduler: MainScheduler.instance)
.subscribe{ (event) in
print("observable_timer ",event);
}
.disposed(by: disposeBag)
// timer2
// 因为没有指定期限 period 。故认定为一次性
Observable<Int>.timer(RxTimeInterval.seconds(1),scheduler: MainScheduler.instance)
.subscribe{(event) in
print("因为没有指定期限 period 。故认定为一次性 ",event)
}
}
7.2 interval
func observable_interval(){
// MARK: timer - interval
// 定时器
Observable<Int>.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
.subscribe{(event) in
print("observable_interval ",event)
}
}
8.Observable
的repeatElement
func observable_repeatElement(){
// MARK: repeatElement
// 该方法创建一个可以无限发出给定元素的event的 Observable 序列(永不终止)
Observable<Int>.repeatElement(5)
.subscribe{(event) in
print("observable_repeatElement ",event)
}
}
9.Observable
的error
func observable_error(){
// MARK: error
// 该方法创建一个可以无线发出给定元素的event的 Observable 序列(永不终止)
Observable<Int>.error(NSError.init(domain: "yyerror", code: 10086, userInfo: ["reason":"unkonw"]))
.subscribe{(event) in
print("observable_error ",event)
}
}
10.Observable
的never
func observable_never(){
// MARK: never
// 该方法创建一个永远不会发出 event(也不会终止的)的 Observable序列
// 这种类型的响应源 在测试 或者在组合操作符中 禁用确切的源非常有用
Observable<Int>.never()
.subscribe{(event) in
print("observable_never ",event)
}
}
③、RxSwift-UI控件
的使用 - 以实际开发控件为例
1.import RxSwift 和 import RxCocoa 说明
import RxSwift // 提供rx的思维
import RxCocoa // 建立RxSwift和UIKit的关系
2.RxSwift-UI控件
UIDatePicker
的使用
YHDataPickerValidator
class YHDataPickerValidator: NSObject {
// 选择的日期是否小于当天;
class func isValidDate(date: Date) -> Bool{
let calendar = NSCalendar.current
let compare = calendar.compare(date,
to: Date.init(),
toGranularity: .day)
return compare == .orderedAscending
}
}
func RxSwift_UIDatePicker(){
self.birthdayPicker.layer.borderWidth = 1;
// 首先我们的出生日期不能早于今天。否则边框变色
let birthdayOB = birthdayPicker.rx.date.map{
YHDataPickerValidator.isValidDate(date: $0) // 返回的是yes 还是no
}
// map 相当于 birthdayOB 是原来的ob 。经过 map 之后变成 另外一个ob
// map 相当于 映射
// f(x) x = 1 f(x)
// x = x + 2 = 3
// 背景色
birthdayOB.map{
$0 ? UIColor.orange : UIColor.red // 返回一个color 然后再订阅
}
.subscribe(onNext:{ (color) in
print(birthdayOB)
self.birthdayPicker.layer.borderColor = color.cgColor
})
.disposed(by: disposeBag)
}
3.RxSwift-UI控件
UIButton
的使用 以选择性别、更新按钮控制为例子
@IBOutlet weak var maleBtn: UIButton!
@IBOutlet weak var femaleBtn: UIButton!
func RxSwift_Btn(birthdayOB:Observable<Bool>){
// 接下来我们处理性别选择
// 现在我们想 性别选择按钮的背后的逻辑
// 1. 性别的选择 和 上面生日的选择 决定下面更新按钮 : 我们常见的必选项
// 2 . 性别的选择 是由我们的两个按钮的处理。我们没必要分开逻辑
let genderSelectOB = BehaviorRelay<YHGender>(value: .notSelcted)
maleBtn.rx.tap
.map{ YHGender.male} // 男生的序列
.bind(to: genderSelectOB) // 绑定到我们定义的序列
.disposed(by: disposeBag)
femaleBtn.rx.tap
.map{ YHGender.female} // 女生的序列
.bind(to: genderSelectOB) // 绑定到我们定义的序列
.disposed(by: disposeBag)
genderSelectOB.asObservable().subscribe(onNext:{(gender) in print(gender)
switch gender{
case .male:
self.maleBtn.setImage(UIImage(named: "check"), for: .normal)
self.femaleBtn.setImage(UIImage(named: "uncheck"), for: .normal)
case .female:
self.femaleBtn.setImage(UIImage(named: "check"), for: .normal)
self.maleBtn.setImage(UIImage(named: "uncheck"), for: .normal)
default:
break;
}
})
.disposed(by: disposeBag)
// 最终的按钮是否能够点击 控制
let genderSELOB = genderSelectOB.asObservable()
.map{$0 != .notSelcted ? true : false}
// 控制我们的点击更新按钮 - 被两个序列共同影响
Observable.combineLatest(birthdayOB,genderSELOB) {$0 && $1}
.bind(to: updateBtn.rx.isEnabled)
.disposed(by: disposeBag)
}
4.RxSwift-UI控件
UISwitch
UISlider
的使用 以Switch开关、UISlider拖动相互关联为例子
@IBOutlet weak var knowSwiftSwitch: UISwitch!
@IBOutlet weak var swiftLevelSlider: UISlider!
func RxSwift_UISwitch(){
// 接下来 我们来玩玩其他控件
/**
对于UISwitch来说:
当UISwitch为OFF时,表示用户不了解Swift,因此,下面的UISlider应该为0;
当UISwitch为ON时,可以默认把UISlider设置在1/4的位置,表示大致了解;
对于UISlider来说:
当UISlider不为0时,应该自动把UISwitch设置为ON;
当UISlider为0时,应该自动把UISwitch设置为OFF;
*/
// on off -> 0.25 0
knowSwiftSwitch.rx.value.map{$0 ? 0.25 : 0}
.bind(to: swiftLevelSlider.rx.value)
.disposed(by: disposeBag)
// 0 1 true flase
swiftLevelSlider.rx.value.map{$0 != 0 ? true : false}
.bind(to: knowSwiftSwitch.rx.isOn)
.disposed(by: disposeBag)
}
5.RxSwift-UI控件
UIStepper
的使用 以UIStepper增加减少关联图片变大变小为例子
@IBOutlet weak var passionToLearnStepper: UIStepper!
@IBOutlet weak var heartHeightConstraint: NSLayoutConstraint! // 图片的约束
func RxSwift_Stepper(){
passionToLearnStepper.rx.value.skip(1)
.subscribe(onNext:{ (value) in
// 关联爱心按钮变大变小
self.heartHeightConstraint.constant = CGFloat(value - 10)
})
.disposed(by: disposeBag)
}