RxSwift-双向绑定

简介: RxSwift-双向绑定

在我们的实际开发中,在MVVM模式下,多数场景是,我们把ViewModel绑定到UI控件上,当ViewModel发生变化时,控件也跟着改变,而有些时候我们要同时实现当我们改变控件值时,ViewModel也跟着变化,这个时候就需要双向绑定,接下来我们就逐步实现一下双向绑定;

说明:双向绑定的ViewModel的属性值一定是基于既是Observable又是Observer


需求描述:有一个文本输入框和一个ViewModel,当文本内容变化的时候,ViewModel里面的值跟随着改变,当ViewModel的值变化的时候,输入框的内容也跟着改变;

首先看ViewModel里面的内容:

struct ViewModel {
    let phoneNumber = BehaviorRelay<String?>(value: nil)
}


phoneNumber变化的时候textFieldtext文本跟着变化:


viewModel.phoneNumber.asObservable().bind(to: textField.rx.text).disposed(by: disposeBag)


textFieldtext文本变化的时候phoneNumber跟着变化:


textField.rx.text.asObservable().bind(to: viewModel.phoneNumber).disposed(by: disposeBag)


最后监听当phoneNumber变化的时候labeltext文本跟着变化,目的仅为了在UI层展示:


viewModel.phoneNumber.asObservable().bind(to: label.rx.text).disposed(by: disposeBag)


上述几步我们通过效果可以看到已经可以实现当任意一个变化的时候都会通知到对方,那么我们可否来封装一下上述操作呢?接下来我们采用自定义操作符的方式来把上述操作封装一下:

首先定义一个运算符:


infix operator <---> : DefaultPrecedence

<--->函数参数一个是用于描述基于UI控件的ControlProperty,另一个是既是Observable又是ObserverBehaviorRelay,最后给定一个返回值,类型为Disposables(此处返回值可有可无、仅为了演示)

func <--->(property: ControlProperty<String?>, relay: BehaviorRelay<String?>) -> Disposable {
    let disposable = relay.bind(to: property)
    let disposable2 = property.asObservable().bind(to: relay)
    return Disposables.create(disposable, disposable2)
}


最后来调用一下,并把结果绑定到label

let _ = textField.rx.text <---> viewModel.phoneNumber
viewModel.phoneNumber.asObservable().bind(to: label.rx.text).disposed(by: disposeBag)


通过运行效果我们可以看到也是满足了我们的需求的;实际RxSwift自带的Demo里面也封装好了用于双向绑定的运算符,可以参考GitHub中对应的Operators.swift文件,接下来我们看一下它的实现:

func <-> <T>(property: ControlProperty<T>, relay: BehaviorRelay<T>) -> Disposable {
    if T.self == String.self {
#if DEBUG && !os(macOS)
        fatalError("It is ok to delete this message, but this is here to warn that you are maybe trying to bind to some `rx.text` property directly to relay.\n" +
            "That will usually work ok, but for some languages that use IME, that simplistic method could cause unexpected issues because it will return intermediate results while text is being inputed.\n" +
            "REMEDY: Just use `textField <-> relay` instead of `textField.rx.text <-> relay`.\n" +
            "Find out more here: https://github.com/ReactiveX/RxSwift/issues/649\n"
            )
#endif
    }
    let bindToUIDisposable = relay.bind(to: property)
    let bindToRelay = property
        .subscribe(onNext: { n in
            relay.accept(n)
        }, onCompleted:  {
            bindToUIDisposable.dispose()
        })
    return Disposables.create(bindToUIDisposable, bindToRelay)
}

可以看到它封装的方法,实现双向绑定的原理都是一致的,内部限制了ControlPropertyBehaviorRelay的参数必须是String类型,否则会抛出异常;绑定的时候通过relay绑定到了property上,当relay改变的时候,property会跟着改变;property通过订阅的形式,当property监听到改变的时候,relay发出信号值也跟着改变;


复制搜一搜分享收藏划线


相关文章
|
iOS开发 开发者
📝 App备案与iOS云管理式证书 ,公钥及证书SHA-1指纹的获取方法
在iOS应用程序开发过程中,进行App备案并获取公钥及证书SHA-1指纹是至关重要的步骤。本文将介绍如何通过appuploader工具获取iOS云管理式证书 Distribution Managed 公钥及证书SHA-1指纹,帮助开发者更好地理解和应用该过程。
|
4月前
|
人工智能 移动开发 小程序
开源啦,1天交付,报价翻5倍,接私活要电子签功能?我把"印章+证据链"封装成可二开底座
开源电子签底座Mini Contract.Pro,聚焦印章管理、落章稳定、证据链留存与多方签署闭环,支持司法级存证与轻量级协作双模式,可私有化部署,助力企业快速构建可控、可证、可换的电子合同系统。
218 4
|
8月前
Vite使用svg-企业级开发(支持本地svg和网络svg渲染)
本教程介绍如何在Vite项目中集成SVG图标插件。首先安装`vite-plugin-svg-icons`,配置插件指向SVG图标目录,并注册全局组件。接着创建SVG图标组件,支持内部图标与外部图片展示。通过简单配置,即可在页面中灵活使用各类SVG图标,提升开发效率。
416 0
|
10月前
|
网络协议 安全 API
WebSocket、Socket、TCP 和 HTTP 的差别与应用场景
WebSocket、Socket、TCP 和 HTTP 是网络通信中的四大“使者”,各具特色:HTTP 适合短时请求,TCP 稳定可靠,Socket 灵活定制,WebSocket 实现实时双向通信。本文用通俗语言解析它们的区别与应用场景,助你为项目选择最合适的通信方式。
3109 3
|
人工智能 API UED
即刻拥有DeepSeek-R1满血版
阿里云推出基于R1满血版DeepSeek的AI解决方案,助您高效学习与工作!访问专属链接进入详情页,按指引免费体验。具体步骤包括:注册阿里云账号、开通百炼模型服务、获取API-KEY、配置Chatbox客户端并测试对话功能。通过简单操作,即可调用DeepSeek-R1模型,实现智能化交互。快速上手,开启AI新体验!
722 7
|
Web App开发 前端开发 Android开发
iOS 唤起 APP: Universal Link(通用链接)(下)
iOS 唤起 APP: Universal Link(通用链接)(下)
2169 0
iOS 唤起 APP: Universal Link(通用链接)(下)
|
人工智能 自然语言处理 程序员
通义灵码内置 DeepSeek V3 和 R1 满血版 671B模型,免费不限量,免部署!
近期,通义灵码能力再升级全新上线模型选择功能,支持基于百炼的 DeepSeek-V3 和 DeepSeek-R1 满血版671B模型,用户可以在 VSCode 和 JetBrains 里搜索并下载最新通义灵码插件,在输入框里选择模型,即可轻松切换模型。
|
安全 数据安全/隐私保护
浏览器如何验证数字证书
【11月更文挑战第2天】本文介绍了数字证书的申请流程及其在HTTPS中的应用。首先,申请者需向CA机构提交包含公钥等信息的表单,经审核后,CA机构使用私钥生成数字签名并返回证书。接着,文章详细描述了浏览器验证证书的过程,包括检查有效期、吊销状态及CA机构的合法性。最后,解释了根CA与中间CA的概念,以及如何通过证书链验证证书的合法性。
Swift 中 struct(结构体)和 class(类)的区别
【10月更文挑战第10天】理解 struct 和 class 的区别对于正确使用 Swift 语言进行编程非常重要。在实际开发中,需要根据具体的需求和场景来选择合适的数据类型,以充分发挥它们的优势,提高代码的质量和效率。
|
存储 Swift
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
770 0