Swift Combine 使用 ObservableObject 与 SwiftUI 模型作为发布源 从入门到精通二十

news2024/12/22 23:41:03

Combine 系列

  1. Swift Combine 从入门到精通一
  2. Swift Combine 发布者订阅者操作者 从入门到精通二
  3. Swift Combine 管道 从入门到精通三
  4. Swift Combine 发布者publisher的生命周期 从入门到精通四
  5. Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
  6. Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
  7. Swift 使用 Combine 进行开发 从入门到精通七
  8. Swift 使用 Combine 管道和线程进行开发 从入门到精通八
  9. Swift Combine 使用 sink, assign 创建一个订阅者 从入门到精通九
  10. Swift Combine 使用 dataTaskPublisher 发起网络请求 从入门到精通十
  11. Swift Combine 用 Future 来封装异步请求 从入门到精通十一
  12. Swift Combine 有序的异步操作 从入门到精通十二
  13. Swift Combine 使用 flatMap 和 catch错误处理 从入门到精通十三
  14. Swift Combine 网络受限时从备用 URL 请求数据 从入门到精通十四
  15. Swift Combine 通过用户输入更新声明式 UI 从入门到精通十五
  16. Swift Combine 级联多个 UI 更新,包括网络请求 从入门到精通十六
  17. Swift Combine 合并多个管道以更新 UI 元素 从入门到精通十七
  18. Swift Combine 通过包装基于 delegate 的 API 创建重复发布者 从入门到精通十八
  19. Swift Combine 响应NotificationCenter 的更新 从入门到精通十九
    在这里插入图片描述

1. 和 SwiftUI 集成 使用 ObservableObject 与 SwiftUI 模型作为发布源

目的: SwiftUI 包含 @ObservedObjectObservableObject 协议,它为 SwiftUI 的视图提供了将状态外部化的手段,同时通知 SwiftUI 模型的变化。

SwiftUI 视图是基于某些已知状态呈现的声明性结构,当该状态发生变化时,这些当前的结构将失效并更新。 我们可以使用 Combine 来提供响应式更新来操纵此状态,并将其暴露回 SwiftUI。 此处提供的示例是一个简单的输入表单,目的是根据对两个字段的输入提供响应式和动态的反馈。

以下规则被编码到 Combine 的管道中:

  1. 两个字段必须相同 - 如输入密码或电子邮件地址,然后通过第二个条目进行确认。
  2. 输入的值至少为 5 个字符的长度。
  3. 根据这些规则的结果启用或禁用提交按钮。

SwiftUI 通过将状态外化为类中的属性,并使用 ObservableObject 协议将该类引用到模型中来实现此目的。 * 两个属性 firstEntrysecondEntry 作为字符串使用 @Published 属性包装,允许 SwiftUI 绑定到它们的更新,以及更新它们。

  • 第三个属性 submitAllowed 暴露为 Combine 发布者,可在视图内使用,从而维护视图内部的 @State buttonIsDisabled 状态
  • 第四个属性 —— 一个 validationMessages 字符串数组 - 在 Combine 管道中将前两个属性进行组合计算,并且使用 @Published 属性包装暴露给 SwiftUI。

SwiftUI-Notes/ReactiveFormModel.swift

import Foundation
import Combine

class ReactiveFormModel : ObservableObject {

    @Published var firstEntry: String = "" {
        didSet {
            firstEntryPublisher.send(self.firstEntry)  // 1
        }
    }
    private let firstEntryPublisher = CurrentValueSubject<String, Never>("")  // 2

    @Published var secondEntry: String = "" {
        didSet {
            secondEntryPublisher.send(self.secondEntry)
        }
    }
    private let secondEntryPublisher = CurrentValueSubject<String, Never>("")

    @Published var validationMessages = [String]()
    private var cancellableSet: Set<AnyCancellable> = []

    var submitAllowed: AnyPublisher<Bool, Never>

    init() {

        let validationPipeline = Publishers.CombineLatest(firstEntryPublisher, secondEntryPublisher)  // 3
            .map { (arg) -> [String] in  // 4
                var diagMsgs = [String]()
                let (value, value_repeat) = arg
                if !(value_repeat == value) {
                    diagMsgs.append("Values for fields must match.")
                }
                if (value.count < 5 || value_repeat.count < 5) {
                    diagMsgs.append("Please enter values of at least 5 characters.")
                }
                return diagMsgs
            }

        submitAllowed = validationPipeline  // 5
            .map { stringArray in
                return stringArray.count < 1
            }
            .eraseToAnyPublisher()

        let _ = validationPipeline  // 6
            .assign(to: \.validationMessages, on: self)
            .store(in: &cancellableSet)
    }
}
  1. firstEntrysecondEntry 都使用空字符串作为默认值。
  2. 然后,这些属性还用 currentValueSubject 进行镜像,该镜像属性使用来自每个 @Published 属性的 didSet 发送更新事件。这驱动下面定义的 Combine 管道,以便在值从 SwiftUI 视图更改时触发响应式更新。
  3. combineLatest 用于合并来自 firstEntrysecondEntry 的更新,以便从任一来源来触发更新。
  4. map 接受输入值并使用它们来确定和发布验证过的消息数组。该数据流 validationPipeline 是两个后续管道的发布源。
  5. 第一个后续管道使用验证过的消息数组来确定一个 truefalse 的布尔值发布者,用于启用或禁用提交按钮。
  6. 第二个后续管道接受验证过的消息数组,并更新持有的该 Observed在这里插入代码片Object 实例的 validationMessages,以便 SwiftUI 在需要时监听和使用它。

两种不同的状态更新的暴露方法 —— 作为发布者或外部状态,在示例中都进行了展示,以便于你可以更好的利用任一种方法。 提交按钮启用/禁用的选项可作为 @Published 属性进行暴露,验证消息的数组可作为 <String[], Never> 类型的发布者而对外暴露。 如果需要涉及作为显式状态去跟踪用户行为,则通过暴露 @Published 属性可能更清晰、不直接耦合,但任一种机制都是可以使用的。

上述模型与声明式地使用外部状态的 SwiftUI 视图相耦合。

SwiftUI-Notes/ReactiveForm.swift

import SwiftUI

struct ReactiveForm: View {

    @ObservedObject var model: ReactiveFormModel  // 1
    // $model is a ObservedObject<ExampleModel>.Wrapper
    // and $model.objectWillChange is a Binding<ObservableObjectPublisher>
    @State private var buttonIsDisabled = true  // 2
    // $buttonIsDisabled is a Binding<Bool>

    var body: some View {
        VStack {
            Text("Reactive Form")
                .font(.headline)

            Form {
                TextField("first entry", text: $model.firstEntry)  // 3
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .lineLimit(1)
                    .multilineTextAlignment(.center)
                    .padding()

                TextField("second entry", text: $model.secondEntry) // 4
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .multilineTextAlignment(.center)
                    .padding()

                VStack {
                    ForEach(model.validationMessages, id: \.self) { msg in  // 4
                        Text(msg)
                            .foregroundColor(.red)
                            .font(.callout)
                    }
                }
            }

            Button(action: {}) {
                Text("Submit")
            }.disabled(buttonIsDisabled)
                .onReceive(model.submitAllowed) { submitAllowed in  // 5
                    self.buttonIsDisabled = !submitAllowed
            }
            .padding()
            .background(RoundedRectangle(cornerRadius: 10)
                .stroke(Color.blue, lineWidth: 1)
            )

            Spacer()
        }
    }
}

struct ReactiveForm_Previews: PreviewProvider {
    static var previews: some View {
        ReactiveForm(model: ReactiveFormModel())
    }
}
  1. 数据模型使用 @ObservedObject 暴露给 SwiftUI。
  2. @State buttonIsDisabled 在该视图中被声明为局部变量,有一个默认值 true
  3. 属性包装($model.firstEntry$model.secondEntry) 的预计值用于将绑定传递到 TextField 视图元素。当用户更改值时,Binding 将触发引用模型上的更新,并让 SwiftUI 的组件知道,如果暴露的模型正在被更改,则组件的更改也即将发生。
  4. 在数据模型中生成和 assign 的验证消息,作为 Combine 管道的发布者,在这儿对于 SwiftUI 是不可见的。相反,这只能对这些被暴露的值的变化所引起的模型的变化做出反应,而不关心改变这些值的机制。
  5. 作为如何使用带有 onReceive 的发布者的示例,使用 onReceive 订阅者来监听引用模型中暴露的发布者。在这个例子中,我们接受值并把它们作为局部变量 @State 存储在 SwiftUI 的视图中,但它也可以在一些转化后使用,如果该逻辑只和视图显示的结果值强相关的话。在这,我们将其与 Button 上的 disabled 一起使用,使 SwiftUI 能够根据 @State 中存储的值启用或禁用该 UI 元素。

参考

https://heckj.github.io/swiftui-notes/index_zh-CN.html

代码

https://github.com/heckj/swiftui-notes

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

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

相关文章

PLC_博图系列☞GRAPH

PLC_博图系列☞GRAPH 文章目录 PLC_博图系列☞GRAPH背景介绍GRAPH优势局限性 顺序控制系统的模块 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 GRAPH 背景介绍 这是一篇关于PLC编程的文章&#xff0c;特别是关于西门子的博图软件。我并不是专业的PLC编程人员&am…

【Redis实战】有MQ为啥不用?用Redis作消息队列!?Redis作消息队列使用方法及底层原理高级进阶

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Redis实战与进阶》 本专栏纯属为爱发电永久免费&#xff01;&a…

报文鉴别、实体鉴别

目录 鉴别 1 报文鉴别 1.1 用数字签名进行鉴别&#xff08;原理&#xff09; 可保证机密性的数字签名 1.2 密码散列函数 MD5 算法 MD5 算法计算步骤 安全散列算法 SHA-1 1.3 用报文鉴别码实现报文鉴别 用报文鉴别码 MAC 鉴别报文 使用已签名的报文鉴别码 MAC 对报…

工程师日常:海丰县附城镇鹿境元宵开灯活动

海丰县附城镇鹿境元宵开灯活动 &#xff08;蔡惠进搜集整理&#xff09; 鹿境乡春节正月初十大老热&#xff0c;全县家喻户晓。为纪念先祖功德&#xff0c;在本乡车地建立蔡氏“济阳堂”大祖祠&#xff0c;并定年初十为开灯日&#xff0c;大祖开灯代代相传。凡移居外乡裔孙、“…

机器学习8-决策树

决策树&#xff08;Decision Tree&#xff09;是一种强大且灵活的机器学习算法&#xff0c;可用于分类和回归问题。它通过从数据中学习一系列规则来建立模型&#xff0c;这些规则对输入数据进行递归的分割&#xff0c;直到达到某个终止条件。 决策树的构建过程&#xff1a; 1.…

java 宠物医院系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 宠物医院系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

463. Island Perimeter(岛屿的周长)

问题描述 给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向相连&#xff08;对角线方向不相连&#xff09;。整个网格被水完全包围&#xff0c;但其中恰好有…

希捷与索尼集团合作生产HAMR写头激光二极管

最近有报道指出&#xff0c;希捷&#xff08;Seagate&#xff09;在生产其采用热辅助磁记录&#xff08;HAMR&#xff09;技术的大容量硬盘时&#xff0c;并非所有组件都在内部制造。根据日经新闻的一份新报告&#xff0c;希捷已与索尼集团合作&#xff0c;由索尼为其HAMR写头生…

qml中边界图片BorderImage的使用

1、基本概念 2、案例 原图&#xff0c;120*120像素 &#xff08;1&#xff09;水平和垂直方向上都设置为拉伸模式 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12ApplicationWindow {id: windowvisible: truew…

【c语言】人生重开模拟器

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

【学习笔记】一文打通Docker!(项目部署orCTF)

Docker What is Docker? 利用Docker 可以快速安装应用&#xff0c;Docker会自动搜索并下载应用镜像(image)。镜像不仅包含应用本身&#xff0c;还包含应用运行所需要的环境&#xff0c;配置&#xff0c;系统函数库。 注意这个系统函数库&#xff0c;相当于在不同的操作版本…

反向迭代器------封装的力量

目录 一、list封装中模板参数Ref和Ptr的理解 二、反向迭代器的实现 一、list封装中模板参数Ref和Ptr的理解 对于反向迭代器&#xff0c;是我们在前面STL模拟实现中留下的一个问题。在之前的文章中&#xff0c;我们极大程度上的利用了模板&#xff0c;从而减少了许多的代码&…

【半监督图像分割 2023 】BHPC

【半监督图像分割 2023 】BHPC 论文题目&#xff1a;Semi-supervised medical image segmentation via hard positives oriented contrastive learning 中文题目&#xff1a;通过面向硬阳性的对比学习进行半监督医学图像分割 论文链接&#xff1a; 论文代码&#xff1a;https:/…

CCF编程能力等级认证GESP—C++6级—20231209

CCF编程能力等级认证GESP—C6级—20231209 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)闯关游戏工作沟通 答案及解析单选题判断题编程题1编程题2 单选题…

Midjourney绘图欣赏系列(五)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

2024023期传足14场胜负前瞻

新的一年祝大家行大运、发大财、中大奖&#xff01;2024023期赛事由英超2场&#xff0c;德甲2场、意甲4场、西甲3场、法甲3场组成。售止时间为2月18日&#xff08;周六&#xff09;21点30分&#xff0c;敬请留意&#xff1a; 本期中深盘较少&#xff0c;1.5以下赔率仅1场&#…

二、ActiveMQ安装

ActiveMQ安装 一、相关环境二、安装Java8三、下载安装包四、启动五、其他命令六、开放端口七、后台管理 一、相关环境 环境&#xff1a;Centos7.9安装ActiveMQ版本&#xff1a;5.15.9JDK8 二、安装Java8 安装教程&#xff1a;https://qingsi.blog.csdn.net/article/details/…

RCS系统之:基础算法

设计仓库机器人的控制管理系统涉及到路径规划、任务分配、库存管理、通信系统等方面。以下是一个基本的仓库机器人控制管理系统方案的概述&#xff1a; 路径规划&#xff1a;设计一个路径规划系统&#xff0c;用于确定机器人在仓库内的最佳行驶路径&#xff0c;以最大程度地提…

学习数据结构和算法的第9天

题目讲解 移除元素 ​ 给你一个数组nums和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val的元素&#xff0c;并返回移除后数组的新长度。 ​ 不要使用额外的数组空间&#xff0c;你必须仅使用0(1)额外空间并 原地 修改输入数组。 ​ 元素的顺序可以改变。你不需要…

使用Taro开发鸿蒙原生应用——快速上手,鸿蒙应用开发指南

导读 本指南为开发者提供了使用 Taro 框架开发鸿蒙原生应用的快速入门方法。Taro&#xff0c;作为一个多端统一开发框架&#xff0c;让开发者能够使用一套代码同时适配多个平台&#xff0c;包括鸿蒙系统。文章将详细介绍如何配置开发环境&#xff0c;以及如何利用 Taro 的特性…