Swift Combine 合并多个管道以更新 UI 元素 从入门到精通十七

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 更新,包括网络请求 从入门到精通十六
    在这里插入图片描述

1. 合并多个管道以更新 UI 元素

目的:观察并响应多个 UI 元素发送的值,并将更新的值联合起来以更新界面。

此示例故意模仿许多 Web 表单样式的验证场景,不过是在 UIKit 中使用 Combine。
在这里插入图片描述

ViewController 被配置了多个通过声明式更新的元素。 同时持有了 3 个主要的文本输入字段:

  • value1
  • value2
  • value2_repeat

它还有一个按钮来提交合并的值,以及两个 labels 来提供反馈。

这些字段的更新规则被实现为:

  • value1 中的条目至少有 3 个字符。
  • value2 中的条目至少有 5 个字符。
  • value2_repeat 中的条目必须与 value2 相同。

如果这些规则中的任何一个未得到满足,则我们希望禁用提交按钮并显示相关消息,解释需要满足的内容。

这可以通过设置连接与合并在一起的一系列管道来实现。

  • 有一个 @Published 属性匹配每个用户输入字段。 combineLatest 用于从属性中获取不断发布的更新,并将它们合并到单个管道中。 map 操作符强制执行所需字符和值必须相同的规则。 如果值与所需的输出不匹配,我们将在管道中传递 nil。
  • value1 还另外有一个验证管道,只使用了 map 操作符来验证值,或返回 nil。
  • 执行验证的 map 操作符内部的逻辑也用于更新用户界面中的 label 信息。
  • 最终管道使用 combineLatest 将两条验证管道合并为一条管道。 此组合的管道上连接了订阅者,以确定是否应启用提交按钮。

下面的示例将这些结合起来进行了展示。
UIKit-Combine/FormViewController.swift

import UIKit
import Combineclass FormViewController: UIViewController {@IBOutlet weak var value1_input: UITextField!@IBOutlet weak var value2_input: UITextField!@IBOutlet weak var value2_repeat_input: UITextField!@IBOutlet weak var submission_button: UIButton!@IBOutlet weak var value1_message_label: UILabel!@IBOutlet weak var value2_message_label: UILabel!@IBAction func value1_updated(_ sender: UITextField) {  // 1value1 = sender.text ?? ""}@IBAction func value2_updated(_ sender: UITextField) {value2 = sender.text ?? ""}@IBAction func value2_repeat_updated(_ sender: UITextField) {value2_repeat = sender.text ?? ""}@Published var value1: String = ""@Published var value2: String = ""@Published var value2_repeat: String = ""var validatedValue1: AnyPublisher<String?, Never> {  // 2return $value1.map { value1 inguard value1.count > 2 else {DispatchQueue.main.async {  // 3self.value1_message_label.text = "minimum of 3 characters required"}return nil}DispatchQueue.main.async {self.value1_message_label.text = ""}return value1}.eraseToAnyPublisher()}var validatedValue2: AnyPublisher<String?, Never> {  // 4return Publishers.CombineLatest($value2, $value2_repeat).receive(on: RunLoop.main)  // 5.map { value2, value2_repeat inguard value2_repeat == value2, value2.count > 4 else {self.value2_message_label.text = "values must match and have at least 5 characters"return nil}self.value2_message_label.text = ""return value2}.eraseToAnyPublisher()}var readyToSubmit: AnyPublisher<(String, String)?, Never> {  // 6return Publishers.CombineLatest(validatedValue2, validatedValue1).map { value2, value1 inguard let realValue2 = value2, let realValue1 = value1 else {return nil}return (realValue2, realValue1)}.eraseToAnyPublisher()}private var cancellableSet: Set<AnyCancellable> = []  // 7override func viewDidLoad() {super.viewDidLoad()self.readyToSubmit.map { $0 != nil }  // 8.receive(on: RunLoop.main).assign(to: \.isEnabled, on: submission_button).store(in: &cancellableSet)  // 9}
}
  1. 此代码的开头遵照了 通过用户输入更新声明式 UI 中的模式. IBAction 消息用于更新 @Published 属性,触发对所连接的任何订阅者的更新。
  2. 第一个验证管道使用 map 操作符接收字符串值输入,如果与验证规则不符,则将其转换为 nil。 这也将发布者属性的输出类型从 <String> 转换为可选的 <String?>。 同样的逻辑也用于触发消息文本的更新,以提供有关所需内容的信息。
  3. 由于我们正在更新用户界面元素,因此我们明确将这些更新包裹在 DispatchQueue.main.async 中,以在主线程上调用。
  4. combineLatest 将两个发布者合并到一个管道中,该管道的输出类型是每个上游发布者的合并值。 在这个例子中,输出类型是 (<String>, <String>) 的元组。
  5. 与其使用 DispatchQueue.main.async,不如使用 receive 操作符明确在主线程上执行下一个操作符,因为它将执行 UI 更新。
  6. 两条验证管道通过 combineLatest 相结合,并将经过检查的输出合并为单个元组输出。
  7. 我们可以将分配的管道存储为 AnyCancellable? 引用(将其映射到 viewcontroller 的生命周期),但另一种选择是创建一个变量来收集所有可取消的引用。 这从空集合开始,任何 sink 或 assign 的订阅者都可以被添加到其中,以持有对它们的引用,以便他们在 viewcontroller 的整个生命周期内运行。 如果你正在创建多个管道,这可能是保持对所有管道的引用的便捷方式。
  8. 如果任何值为 nil,则 map 操作符将向管道传递 false 值。 对 nil 值的检查提供了用于启用(或禁用)提交按钮的布尔值。
  9. store 方法可在 Cancellable 协议上调用,该协议明确设置为支持存储可用于取消管道的引用。在这里插入代码片

参考

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

代码

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)

文章目录 2.1 条件语句&#xff1a;决策的艺术2.1.1 基础知识讲解2.1.2 重点案例&#xff1a;用户角色权限判断实现用户角色权限判断扩展功能实现代码功能扩展&#xff1a;添加或删除用户 2.1.3 拓展案例 1&#xff1a;成绩等级判断实现成绩等级判断功能实现代码扩展功能&#…

C++初阶(十一) list

一、list的介绍及使用 1.1 list的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点…

《软件方法(下)》8.2.5.1 类名中是否有形容词(202402更新)

8.2.5.1 类名中是否有形容词 如果存在“形容词&#xff08;的&#xff09;名词”这样的类名&#xff0c;例如“待支付&#xff08;的&#xff09;订单”、“合适&#xff08;的&#xff09;会议室”&#xff0c;可以先把形容词从类名移除&#xff0c;转成类的一个状态属性。很…

Python iter函数

在Python编程中&#xff0c;iter()函数是一个非常重要且常用的内置函数&#xff0c;用于生成迭代器对象。迭代器是一种可以逐个访问数据元素的对象&#xff0c;可以用于遍历序列、集合以及自定义数据结构等。本文将深入探讨Python中的iter()函数&#xff0c;包括基本用法、可迭…

AI:129-基于深度学习的极端天气事件预警

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

Java集合 List接口

List接口操作 Java的List接口是Java集合框架中的一部分&#xff0c;它表示有序的集合。List接口提供了许多常用的方法&#xff0c;以下是其中的一些例子&#xff1a; 增加元素 add(E e)&#xff1a;将指定的元素插入此列表的末尾。 List<String> list new ArrayList…

【JavaEE】spring boot快速上手

SpringBoot快速上手 文章目录 SpringBoot快速上手Maven会出现的一个官方bug创建完项目之后常用的的三个功能依赖管理Maven仓库中央仓库本地仓库国内源配置私服 springboot项目创建什么是springspring boot项目的创建Hello Worldweb服务器 SpringMVC什么是SpringWebMVC什么是MVC…

LabVIEW卫星电视接收仿真系统

LabVIEW卫星电视接收仿真系统 随着卫星电视数字化的加速&#xff0c;传统模拟信号接收系统已无法满足需求。设计一套船载数字卫星电视接收系统&#xff0c;通过LabVIEW环境进行仿真实验&#xff0c;验证系统设计的可行性与有效性&#xff0c;满足数字信号接收的高精度要求&…

RUST入门:如何用vscode调试rust程序

RUST已经流行一阵子了&#xff0c;但是比较系统的IDE介绍还是比较少&#xff0c;这里我简单介绍 一下如何用vscode实现单步调试rust程序&#xff0c;就像我们平时调试c程序一样。 学习资料网站 首先&#xff0c;介绍几个学习rust的好网站&#xff0c; Rust程序设计语言Rust语…

【FPGA开发】HDMI通信协议解析及FPGA实现

本篇文章包含的内容 一、HDMI简介1.1 HDMI引脚解析1.2 HDMI工作原理1.3 DVI编码1.4 TMDS编码 二、并串转换、单端差分转换原语2.1 原语简介2.2 IO端口组件 笔者在这里使用的开发板是正点原子的达芬奇开发板&#xff0c;FPGA型号为XC7A35TFGG484-2。参考的课程是正点原子的课程手…

【研究生复试】计算机软件工程人工智能研究生复试——资料整理(速记版)——数据库

1、JAVA 2、计算机网络 3、计算机体系结构 4、数据库 5、计算机租场原理 6、软件工程 7、大数据 8、英文 自我介绍 4. 数据库 1. B树相对于B树的区别及优势 B树中有重复元素&#xff0c;B树没有重复元素B树种每个节点都存储了key和data&#xff0c;B树内节点去掉了其中指向数…

macOS 安装 conda

macOS 安装 conda 安装 conda参考 Conda是一个开源的软件包管理系统和环境管理系统&#xff0c;用于安装和管理软件包和其依赖项。 安装 conda mkdir miniconda3 cd miniconda3 bash Miniconda3-latest-MacOSX-x86_64.sh$ conda list参考 macOS 安装 conda开始使用conda