Swift知识点---RxSwift学习

news2025/1/10 11:16:03

1. 什么是RxSwift

RxSwift是Swift函数响应式编程的一个开源库,由Github的ReactiveX组织开发、维护

RxSwift的目的是:让数据/事件流 和 异步任务能够更方便的序列化处理,能够使用Swift进行响应式编程

RxSwift本质上还是观察者模式,并且是一个响应式的,并且可以序列化

观察者模式

观察者模式包括:KVO、通知等

爸爸妈妈照看观察宝宝
其中,宝贝就是被观察者
爸爸妈妈就是观察者,或者说是订阅者
只要被观察者(宝宝)发出来某些事件,比如哭声、叫声,则被称为事件,通知到订阅者
此时,订阅者就可以做响应的工作

RxSwift做了什么?

RxSwift把我们程序中的每一个操作都看成一个事件
比如一个TextField中的文本改变、一个按钮被点击、一个网络请求结束等
每一个事件源就可以看成一个管道,也就是sequence

比如,TextField,当我们改变里面的文本的时候,这个TextField就会不断的发出事件
从他的sequence中不断流出,我们只需要监听这个sequence,每流出一个事件就做相应的处理

同理,Button也是一个sequence,每点击一次就流出一个事件

理解Observable和Observer

在这里插入图片描述


2. RxSwift简单体验

  • RxSwift监听按钮的点击
  • RxSwift监听UITextField的文字改变
  • RxSwift改变Label中的文字
  • RxSwift监听对象属性改变
  • RxSwift监听UIScrollView的滚动
//导入RxSwift
import RxSwift
import RxCocoa

按钮监听

原始方法:
button.addTarget(self, action: #selector(button1Click), for: .touchUpInside)

使用RxSwift的方法:

self.button.rx.tap.subscribe { (event: Event<Void>) in
	print(event)
}

上述方法会有一个标黄的警告:Result of call to ‘subscribe’ is unused
修改方法:

private lazy var disposeBag: DisposeBag = DisposeBag()

self.button.rx.tap.subscribe { (event: Event<Void>) in
      print(event)
}.disposed(by: disposeBag)```

监听UITextField文字的改变

方法一:传统方法

方法二:
self.view.addSubview(self.textField)
self.textField.frame = CGRect(x: 200, y: 300, width: 100, height: 40)
self.textField.rx.text.subscribe { (event: Event<String?>) in
	print(event.element)//获取信息
}.disposed(by: disposeBag)

这种方法,获取的event有两个Optional包裹着,使用起来不方便

方法三:
self.textField.rx.text.subscribe { (myString: String?) in
    print(myString)//获取的是Optional类型
} onError: { error in
    print(error)
} onCompleted: {
    print("onCompleted")
} onDisposed: {
    print("onDisposed")
}.disposed(by: disposeBag)

以上可以简化:

self.textField.rx.text.subscribe { (myString: String?) in
    print(myString)//获取的是Optional类型
}.disposed(by: disposeBag)

监听Label中的文字

UITextField文字输入,然后Label显示输入的文字

方法一:
self.textField.rx.text.subscribe { (myString: String?) in
    print(myString)
    self.myTitleLabel.text = myString
}.disposed(by: disposeBag)

self.view.addSubview(myTitleLabel)
myTitleLabel.frame = CGRect(x: 100, y: 210, width: 200, height: 40)
方法二:
self.textField.rx.text
    .bind(to: myTitleLabel.rx.text)
    .disposed(by: disposeBag)
    
self.view.addSubview(myTitleLabel)
myTitleLabel.frame = CGRect(x: 100, y: 210, width: 200, height: 40)

RxSwift的KVO

self.view.addSubview(myTitleLabel)
myTitleLabel.frame = CGRect(x: 100, y: 210, width: 200, height: 40)
        
myTitleLabel.rx.observe(String.self, "text").subscribe { (str: String?) in
	print(str)
}.disposed(by: disposeBag)

RxSwift监听UIScrollView的滚动

self.view.addSubview(scrollView)
scrollView.frame = CGRect(x: 100, y: 280, width: 200, height: 150)
scrollView.rx.contentOffset.subscribe { (point: CGPoint) in
	print(point)
}.disposed(by: disposeBag)

3. RxSwift常见操作

Never、Empty、Just、Of、From、Create、Range、RepeatElement

  • Never,什么都不执行
///never,啥事没有
let observableNever = Observable<String>.never()
observableNever.subscribe { (event: Event<String>) in
	print(event)
}.disposed(by: disposeBag)
  • Empty创建一个空的sequence,只能发出一个completed事件
 //只执行completed
let observableEmpty = Observable<String>.empty()
observableEmpty.subscribe { (event: Event<String>) in
	print(event)
}.disposed(by: disposeBag)
        
打印结果:        
completed
  • Just是创建一个sequence,只能发出一种特定的事件,并且能正常结束

也就是,特定事件可以监听,complete事件可以监听

///Just:只执行特定类型+complete
let observableJust = Observable.just(123)//Int类型
observableJust.subscribe { (event: Event<Int>) in//Event<Int>相对应
	print(event)
}.disposed(by: disposeBag)

打印结果:     
next(123)
completed
  • Of可以执行特定类型+complete
  • 创建一个sequence,可以发出多个事件信号
///Of: 可以执行特定类型多个事件+complete
let observableOf = Observable.of("1", "2", "3", "6")
observableOf.subscribe { (event: Event<String>) in
	print(event)
}.disposed(by: disposeBag)

打印结果:          
next(1)
next(2)
next(3)
next(6)
completed
  • From从数组中创建sequence
///From: 可以执行数组+complete
let observableFrom = Observable.from(["123", "234", "345"])
observableFrom.subscribe { (event: Event<String>) in
    print(event)
}.disposed(by: disposeBag)

打印结果:
next(123)
next(234)
next(345)
completed
  • Create可以自定义可观察的sequence
  • Create操作符传入一个观察者observer,然后调用observer的onNext,onCompleted和onError方法,返回一个可观察的observable序列
///Create: 可以自定义观察事件+complete
let observableCreate = createObservableFunc()
observableCreate.subscribe { (event: Event<Any>) in
	print(event)
}.disposed(by: disposeBag)

///创建自定义的createObservable
private func createObservableFunc() -> Observable<Any> {
	return Observable.create { (observer: AnyObserver<Any>) in
		observer.onNext("123")
        observer.onNext("321")
        //注意:Disposables,带s
        return Disposables.create()
    }
}
打印结果:
next(123)
next(321)

如果想要completed,则需要加上:observer.onCompleted()

  • Range创建一个sequence,会发出这个范围中的从开始到结束的所有事件
///Range: 可以执行某个范围内的
let observableRange = Observable.range(start: 3, count: 3)
observableRange.subscribe { (event: Event<Int>) in
    print(event)
}.disposed(by: disposeBag)

打印结果:
next(3)
next(4)
next(5)
completed
  • RepeatElement重复执行某个事件
///RepeatElement: 重复执行某个事件
let observableRepeatElement = Observable.repeatElement("2")
observableRepeatElement.subscribe { (event: Event<String>) in
    print(event)
}.disposed(by: disposeBag)

执行以上代码,恭喜你,程序干崩溃了= =

可以加上take(count)

///RepeatElement: 重复执行某个事件
let observableRepeatElement = Observable.repeatElement("2")
observableRepeatElement
    .take(3)
    .subscribe { (event: Event<String>) in
        print(event)
    }
    .disposed(by: disposeBag)

打印结果:
next(2)
next(2)
next(2)
completed

4. RxSwift中Subjects

Subjects是什么?

Subject是Observable和Observer之间的桥梁
一个Subject既是一个Observable,也是一个Observer
即可以监听事件,也可以发出事件

Observable:监听事件
Observer:发出事件

PublishSubject、ReplaySubject、BehaviorSubject、BehaviorRelay

PublishSubject

当订阅PublishSubject的时候,只能接收订阅他之后发生的事件
subject.onNext()发出onNext事件,对应的还有OnError()和onCompleted()事件

let publishSub = PublishSubject<String>()
//不接收
publishSub.onNext("0")

//订阅事件
publishSub.subscribe { (event: Event<String>) in
    print(event)
}.disposed(by: disposeBag)


//只能接收订阅后的事件
publishSub.onNext("1")
publishSub.onNext("2")
publishSub.onNext("3")
publishSub.onCompleted()

打印结果:
next(1)
next(2)
next(3)
completed

ReplaySubject

当你订阅ReplaySubject的时候,你可以接收到订阅他之后的事件
但也可以接受订阅他之前发出的事件,接受前面几个事件,取决与bufferSize的大小

//bufferSize决定订阅前接收几个。订阅后的都可以接收到
let replaySbu = ReplaySubject<String>.create(bufferSize: 2)
replaySbu.onNext("1-0")
replaySbu.onNext("1-1")
replaySbu.onNext("1-2")
//订阅事件
replaySbu.subscribe { (event: Event<String>) in
    print(event)
}.disposed(by: disposeBag)
replaySbu.onNext("1-3")
replaySbu.onNext("1-4")
replaySbu.onNext("1-5")
replaySbu.onCompleted()

打印结果:
next(1-1)
next(1-2)
next(1-3)
next(1-4)
next(1-5)
completed

BehaviorSubject

BehaviorSubject会接受到订阅之前的最后一个事件

//behaviorSbu接收订阅前最后一个。订阅后的都可以接收到
let behaviorSbu = BehaviorSubject(value: "1")
behaviorSbu.onNext("2-0")
behaviorSbu.onNext("2-1")
behaviorSbu.onNext("2-2")
//订阅事件
behaviorSbu.subscribe { (event: Event<String>) in
    print(event)
}.disposed(by: disposeBag)
behaviorSbu.onNext("2-3")
behaviorSbu.onNext("2-4")
behaviorSbu.onNext("2-5")
behaviorSbu.onCompleted()

打印结果:
next(2-2)
next(2-3)
next(2-4)
next(2-5)
completed

BehaviorRelay

behaviorRelay是BehaviorSubject的一个包装箱
使用的时候,需要调用asObservable()拆箱,里面的value是一个BehaviorSubject
如果Variable准备发出事件,不需要onNext这种方式,而是直接修改对象的value即可

let behaviorRelay = BehaviorRelay(value: "3-1")
behaviorRelay.accept("3-2")
behaviorRelay.accept("3-3")
behaviorRelay.asObservable().subscribe { (event: Event<String>) in
    print(event)
}.disposed(by: disposeBag)
behaviorRelay.accept("3-4")
behaviorRelay.accept("3-5")

打印结果:
next(3-3)
next(3-4)
next(3-5)

RxSwift在UITableView的使用

方法一:

Model:

struct HeroModel: Codable {
    var name: String
    var age: String
    var description: String
    
    enum CodingKeys: String, CodingKey {
        case name
        case age
        case description = "describe"
    }
}

ViewModel:

class HeroViewModel {
    var herosObserrable: BehaviorRelay<[HeroModel]> = {
        var herosArray: [HeroModel] = [HeroModel]()
        
        guard
            let path = Bundle.main.path(forResource: "RxSwiftData", ofType: "json"),
            let jsonData = try? Data(contentsOf: URL(fileURLWithPath: path))
        else {
            print("Error loading JSON file")
            return BehaviorRelay(value: [])
        }
        
        do {
            let decoder = JSONDecoder()
            herosArray = try decoder.decode([HeroModel].self, from: jsonData)
            
            for hero in herosArray {
                print(hero)
            }
        }
        catch {
            print("Error parsing JSON: \(error)")
        }
        
        return BehaviorRelay(value: herosArray)
    }()
}

extension ViewController {
    fileprivate func loadData(){
    	//获取VM
        let heroVM = HeroViewModel()
        //订阅VM
        heroVM.herosObserrable.asObservable().subscribe { (heros: [HeroModel]) in
            self.herosArray.removeAll()
            self.herosArray = heros
            self.myTableView.reloadData()
        }.disposed(by: disposeBag)
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return herosArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell") as? MyTableViewCell else {
            print("Error: Could not dequeue cell as MyTableViewCell")
            return UITableViewCell()
        }
        
        cell.nameLabel.text = herosArray[indexPath.row].name
        cell.ageLabel.text = herosArray[indexPath.row].age
        cell.desLabel.text = herosArray[indexPath.row].description
        
        return cell
    }
}

private lazy var myTableView: UITableView = {
    let myTableView = UITableView()
    myTableView.delegate = self
    myTableView.dataSource = self
    myTableView.register(MyTableViewCell.self, forCellReuseIdentifier: "MyTableViewCell")
    return myTableView
}()

方法二:

Model、ViewModel同上

extension ViewController {
    fileprivate func loadData(){

        let heroVM = HeroViewModel()
        //直接在这,将监听的数据,直接赋值
        heroVM.herosObserrable.asObservable()
            .bind(to: myTableView.rx.items(cellIdentifier: "MyTableViewCell", cellType: MyTableViewCell.self)){
                (row, hero, cell) in
                
                cell.nameLabel.text = hero.name
                cell.ageLabel.text = hero.age
                cell.desLabel.text = hero.description
            }.disposed(by: disposeBag)
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2112808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

栈—数据结构

一、系统栈 系统栈&#xff08;System Stack&#xff09;&#xff1a; 用途&#xff1a;系统栈通常指的是调用栈&#xff08;Call Stack&#xff09;&#xff0c;它用于存储程序执行期间的函数调用信息。每当一个函数被调用时&#xff0c;系统栈会记录这个调用的状态&#xff0…

notepad下载安装使用以及高级使用技巧

前言 Notepad是一款广受欢迎的文本编辑器&#xff0c;尤其受到开发者和编程人员的喜爱。它支持多种编程语言的语法高亮显示&#xff0c;并且提供了丰富的插件系统&#xff0c;使得功能可以轻松扩展。本文将详细介绍如何在Windows操作系统上下载、安装Notepad&#xff0c;以及基…

数据结构与算法 第11天 (查找)

一、概念 查找表 查找表是一个集合 静态查找表&#xff1a;查找完没变化 动态查找表&#xff1a;进行插入删除操作 主关键字 类似主键能唯一确定一个元素 平均查找长度ASL average search length 评价查找算法的指标 查找目的 1、查询某个“特定的”数据元素是…

Ifream实现微前端效果

记得有人曾问过我&#xff0c;老旧的项目内容很多&#xff0c;项目卡&#xff0c;想要改造成类似微前端&#xff0c;领导想要快速&#xff0c;又不想系统重构、而且是不同子系统的协同&#xff0c;要怎么做&#xff1f;对方不想做太大的改造&#xff0c;所以想用ifream的方式动…

图片隐写方法

1、常规隐写 思路&#xff1a; 1、文件头部被破坏&#xff1b;修复文件头部 2、16进制异或&#xff08;1E&#xff09; 3、宽高被修改&#xff1b;修复宽高&#xff1b;使用python脚本获取宽高或者使用tweakpng工具获取宽高 4、图片转化成base编码&#xff1b;让你还原图片 5、…

MySQL数据库的基本使用

目录 1.MySQL数据库中的用户管理与登录 1.1用户管理 root用户 其他用户 1.2MySQL数据库的登录 2.使用MySQL的前置知识 2.1SQL语言 SQL语言简介 SQL语句的分类 2.2编码集的认识 字符集 校验集 字符集和校验集的关系 如何查看和修改字符集与校验集 3.MySQL数据库的…

【Pyhton报错已解决】`AttributeError: ‘list‘ object has no attribute ‘text‘`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言&#xff1a;一、问题描述&#xff1a;1.1 报错示例&#xff1a;1.2 报错分析&#xff1a;1.3 解决思路&#xff…

2024 年,数据中台引领企业走向何方?

2024 年&#xff0c;数据中台引领企业走向何方&#xff1f; 前言数据中台引领企业走向何方 前言 在当今数字化时代&#xff0c;数据已成为企业发展的核心资产。随着企业业务的不断扩展和数据量的急剧增长&#xff0c;如何有效地管理和利用数据&#xff0c;成为企业面临的重要挑…

计算机论文七种流程图画法,详细教程来袭!(导师都说我图画的标准)

我 | 在这里 ⭐ 全栈开发攻城狮、全网10W粉丝、2022博客之星后端领域Top1、专家博主。 &#x1f393;擅长 指导毕设 | 论文指导 | 系统开发 | 毕业答辩 | 系统讲解等。已指导60位同学顺利毕业 ✈️个人公众号&#xff1a;热爱技术的小郑。回复 Java全套视频教程 或 前端全套视频…

Linux学习~树莓派gpio控制(1)

git clone git://git.drogon.net/wiringPi cd wiringPi ./build build 脚本会帮助你编译和安装 wiringPi。 方案B——直接下载 tar xfz wiringPi-xx.tar.gz cd wiringPi-xx ./build wiringPi 包括一套 gpio 命令&#xff0c;使用 gpio 命令可以控制树莓派上的各种接口&…

【物理密度计工作原理图】密度大小与密度计浸没深度关系图

密度大小与密度计浸没深度关系图 绘制图像的好处&#xff1a; 直观展示数据&#xff1a;图形可以直观地展示数据之间的关系&#xff0c;使得理解和分析数据变得更加容易。 便于比较&#xff1a;通过图形可以快速比较不同液体密度下密度计的浸没深度变化。 科学验证&#xff…

我的私人助理 | 办公小浣熊

我的私人助理 | 办公小浣熊 办公小浣熊上手实操业务场景分析一&#xff08;数据处理&#xff09;demo 本地数据库操作业务场景分析二&#xff08;数据处理&#xff09;Excel 本地文件操作业务场景分析三&#xff08;数据可视化&#xff09;业务场景分析四&#xff08;趋势判断&…

【B题第二套完整论文已出】2024数模国赛B题第二套完整论文+可运行代码参考(无偿分享)

2024数模国赛B题完整论文 摘要&#xff1a; 随着电子产品制造业的快速发展&#xff0c;质量控制与成本优化问题成为生产过程中亟待解决的核心挑战。为应对生产环节中的质量不确定性及成本控制需求&#xff0c;本文结合抽样检测理论和成本效益分析&#xff0c;通过构建数学模型…

Leetcode面试经典150题-128.最长连续序列-递归版本另解

之前写过一篇这个题的&#xff0c;但是可能代码比较复杂&#xff0c;这回来个简洁版的&#xff0c;这个是递归版本 可以看看之前的版本&#xff0c;两个版本面试用哪个都保过 解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {/**对于之前的解法&#xff0c;我…

【vulhub】thinkphp5 2-rce 5.0.23-rce 5-rce 漏洞复现

2-rec 1.启动环境 cd /.../vulhub/thinkphp/2-rce # cd进入2-rce靶场文件环境下 docker-compose up -d # docker-compose启动靶场 docker ps -a # 查看开启的靶场信息2.访问192.168.146.136&#xff1a;8080网页 3.构造payload http://192.168.146.136:80…

Openharmony 下载到rk3568实现横屏

前言&#xff1a; Openharmony 源码版本4.1 release 板子&#xff1a;rk3568 1.修改“abilities”中的“orientation”实现横竖屏 entyr->src->module.json5文件里面添加 "orientation": "landscape", 2.修改系统源码属性实现横竖屏切换 通过这…

IDEA取消自动选择光标所在行

今天出现了一个怪事&#xff1a; 当我使用IDEA编写代码的时候&#xff0c;单击下一行或者上一行的时候&#xff0c;莫名其妙它会自己选中一行&#xff0c;导致我要么是回车代码直接没了&#xff0c;要么是代码直接给我搞错位了&#xff0c;还得按ctrlz返回&#xff0c;十分的恶…

【C++】:模板初阶—函数模板|类模板

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山岗&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 本文参考博客&#xff1a;一同感受C模版的所带来的魅力 一、泛型编程思想 首先…

代码随想录算法训练营第五十一天 | 99.岛屿数量-深搜 ,99.岛屿数量-广搜 ,100.岛屿的最大面积

目录 99.岛屿数量-深搜 思路 方法一&#xff1a; 深度优先搜索-先判断 方法二&#xff1a;深度优先搜索-终止条件 心得收获 99.岛屿数量-广搜 思路 广度优先搜索 方法一&#xff1a;广度优先搜索 100.岛屿的最大面积 思路 深度优先搜索 广度优先搜索 方法一&am…

c语言——用一维数组输出杨辉三角形

一.代码 #include <stdio.h> int Num[100]; int Hang; int Lie; int a; int Flag; int main() {Lie 1;Hang 1;a 0;while (1) {//列1为1if (Lie 1) {Num[1] 1;Lie;}//数据存到数组里面while (Hang > Lie && Hang ! 2) { if (Hang!Lie) {Flag Num[Lie] …