Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出

2023-10-05 20:16

本文主要是介绍Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

0. 概览

Swift 5.9 一声炮响为我们带来全新的宏(Macro)机制,也同时带来了干霄凌云的 Observation 框架。

在这里插入图片描述

Observation 框架可以增强通用场景下的使用,也可以搭配 SwiftUI 5.0 而获得双剑合璧的更强威力。

在本篇博文,您将学到如下内容:

  • 0. 概览
  • 1. @Observable 宏
  • 2. 通用情境下如何观察 Observable 对象?
  • 3. Observable 对象与 SwiftUI 珠联璧合
  • 4. 被“抛弃的” @EnvironmentObject
  • 5. 在视图中将不可变 Observable 对象转换为可变对象的妙招
  • 6. 总结

那么,就让我们赶快进入 Observation 奇妙的世界吧!

Let‘s go!!!😉


1. @Observable 宏

简单来说,Observation 框架为我们提供了集鲁棒性(robust)、安全性、高性能等三大特性为一身的 Swift 全新观察者设计模式。

它的核心功能在于:监视对象状态,并在改变时做出反应!

在这里插入图片描述

在 Swift 5.9 中,我们可以非常轻松的通过 @Observable 宏将普通类“转化为”可观察(Observable)类。自然,它们的实例都是可观察的:

@Observable
final class Hero {var name: Stringvar power: Intinit(name: String, power: Int) {self.name = nameself.power = power}
}@Observable
final class Model {var title: Stringvar createAt: Date?var heros: [Hero]init(title: String, heros: [Hero]) {self.title = titleself.createAt = Date.nowself.heros = heros}
}

如上代码所示,我们定义了两个可观察类 Model 和 Hero,就是这么简单!

2. 通用情境下如何观察 Observable 对象?

在一个对象成为可观察之后,我们可以通过 withObservationTracking() 方法随时监听它状态的改变:

在这里插入图片描述

我们可以将对象需要监听的属性放在 withObservationTracking() 的 apply 闭包中,当且仅当( Hero 中其它属性的改变不予理会)这些属性发生改变时其 onChange 闭包将会被调用:

let hero = Hero(name: "大熊猫侯佩", power: 5)func watching() {withObservationTracking({NSLog("力量参考值:\(hero.power)")}, onChange: {NSLog("改变之前的力量!:\(hero.power)")watching()})
}watching()hero.name = "地球熊猫"
hero.power = 11
hero.power = 121

以上代码输出如下:

在这里插入图片描述

使用 withObservationTracking() 方法有 3 点需要注意:

  1. 它默认只会被调用 1 次,所以上面为了能够重复监听,我们在 onChange 闭包里对 watching() 方法再次进行了调用;
  2. withObservationTracking() 方法的 apply 闭包不管如何都会被调用 1 次,即使其监听的属性从未改变过;
  3. 在监听闭包中只能得到属性改变前的旧值;

目前,上面测试代码在 Xcode 15 的 Playground 中编译会报错,提示如下:

error: test15.playground:8:13: error: external macro implementation type ‘ObservationMacros.ObservableMacro’ could not be found for macro ‘Observable()’
final class Hero {
^

Observation.Observable:2:180: note: ‘Observable()’ declared here
@attached(member, names: named(_$observationRegistrar), named(access), named(withMutation)) @attached(memberAttribute) @attached(extension, conformances: Observable) public macro Observable() = #externalMacro(module: “ObservationMacros”, type: “ObservableMacro”)

小伙伴们可以把它们放在 Xcode 的 Command Line Tool 项目中进行测试:

在这里插入图片描述


3. Observable 对象与 SwiftUI 珠联璧合

要想发挥 Observable 对象的最大威力,我们需要 SwiftUI 来一拍即合。

在 SwiftUI 中,我们无需再显式调用 withObservationTracking() 方法来监听改变,如虎添翼的 SwiftUI 已为我们自动完成了所有这一切!

struct ContentView: View {let model = Model(title: "地球超级英雄", heros: [])var body: some View {        NavigationStack {Form {LabeledContent(content: {Text(model.title)}, label: {Text("藏匿点名称")})LabeledContent(content: {Text(model.createAt?.formatted(date: .omitted, time: .standard) ?? "无")}, label: {Text("更新时间")})Button("刷新") {// SwiftUI 会自动监听可观察对象的改变,并刷新界面model.title = "爱丽丝仙境兔子洞"model.createAt = Date.now}}.navigationTitle(model.title)}}
}

注意,上面代码中 model 属性只是一个普通的 let 常量,即便如此 model 的改变仍会反映到界面上:

在这里插入图片描述

4. 被“抛弃的” @EnvironmentObject

有了 Swift 5.9 中新 Observation 框架加入游戏,在 SwiftUI 5.0 中 EnvironmentObject 再无用武之地,我们仅用 Environment 即可搞定一切!

早在 SwiftUI 1.0 版本时,其就已经提供了 Environment 对应的构造器:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct Environment<Value> : DynamicProperty {...}

有了新 Observation 框架的入驻,结合其 Observable 可观察对象,Environment 可以再次大放异彩:

struct HeroListView: View {@Environment(Model.self) var modelvar body: some View {List(model.heros) { hero inHStack {Text(hero.name).font(.headline)Spacer()Text("\(hero.power)").font(.subheadline).foregroundStyle(.gray)}}}
}struct ContentView: View {@State var model = Model(title: "地球超级英雄", heros: [.init(name: "大熊猫侯佩", power: 5),.init(name: "孙悟空", power: 1000),.init(name: "哪吒", power: 511)])var body: some View {        NavigationStack {Form {NavigationLink(destination: HeroListView().environment(model)) {Text("查看所有英雄")}}.navigationTitle(model.title)}}
}

现在,即使跨越多重层级关系我们也可以只通过 @Environment 而不用 @EnvironmentObject 来完成状态的间接传递了,是不是很赞呢?👍🏻

5. 在视图中将不可变 Observable 对象转换为可变对象的妙招

介绍了以上这许多,就还剩一个主题没有涉及:**Observable 对象的可变性!
**

为了能够在子视图中更改对应的可观察对象,我们可以用 @Bindable 修饰传入的 Observable 对象:

struct HeroView: View {@Bindable var hero: Herovar body: some View {Form {TextField("名称", text: $hero.name)TextField("力量", text: .init(get: {String(hero.power) }, set: {hero.power = Int($0) ?? 0}))}}
}

不过,对于之前 @Environment 那个例子来说,如何达到子视图能够修改传入的 @Environment 可观察对象呢?

别急,我们可以利用称为“临时可变(Temporary Variable)”的技术将原先不可变的可观察对象改为可变:

extension Hero: Identifiable {var id: String {name}
}struct HeroListView: View {@Environment(Model.self) var modelvar body: some View {// 在 body 内将 model 改为可变@Bindable var model = modelVStack {List(model.heros) { hero inHStack {Text(hero.name).font(.headline)Spacer()Text("\(hero.power)").font(.subheadline).foregroundStyle(.gray)}}.safeAreaInset(edge: .bottom) {// 绑定可变 model 中的状态以修改英雄名称TextField("", text: $model.heros[0].name).padding()} }}
}

运行效果如下:

在这里插入图片描述

“临时可变”这一技术可以用于视图中任何化“不变”为“可变”的场景中,当然对于直接视图间对象的传递,我们可以使用 @Bindable 这一更为“正统”的方法。

6. 总结

在本篇博文中,我们讨论了在 Swift 5.0 和 SwiftUI 5.0 中大放异彩 Observation 框架的使用,并就诸多技术细节问题给与了详细的介绍,愿君喜欢。

感谢观赏,再会!😎

这篇关于Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/151960

相关文章

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Python Flask 库及应用场景

《PythonFlask库及应用场景》Flask是Python生态中​轻量级且高度灵活的Web开发框架,基于WerkzeugWSGI工具库和Jinja2模板引擎构建,下面给大家介绍PythonFl... 目录一、Flask 库简介二、核心组件与架构三、常用函数与核心操作 ​1. 基础应用搭建​2. 路由与参

Spring Boot中的YML配置列表及应用小结

《SpringBoot中的YML配置列表及应用小结》在SpringBoot中使用YAML进行列表的配置不仅简洁明了,还能提高代码的可读性和可维护性,:本文主要介绍SpringBoot中的YML配... 目录YAML列表的基础语法在Spring Boot中的应用从YAML读取列表列表中的复杂对象其他注意事项总

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

CSS 样式表的四种应用方式及css注释的应用小结

《CSS样式表的四种应用方式及css注释的应用小结》:本文主要介绍了CSS样式表的四种应用方式及css注释的应用小结,本文通过实例代码给大家介绍的非常详细,详细内容请阅读本文,希望能对你有所帮助... 一、外部 css(推荐方式)定义:将 CSS 代码保存为独立的 .css 文件,通过 <link> 标签

SpringBoot基础框架详解

《SpringBoot基础框架详解》SpringBoot开发目的是为了简化Spring应用的创建、运行、调试和部署等,使用SpringBoot可以不用或者只需要很少的Spring配置就可以让企业项目快... 目录SpringBoot基础 – 框架介绍1.SpringBoot介绍1.1 概述1.2 核心功能2

Python使用Reflex构建现代Web应用的完全指南

《Python使用Reflex构建现代Web应用的完全指南》这篇文章为大家深入介绍了Reflex框架的设计理念,技术特性,项目结构,核心API,实际开发流程以及与其他框架的对比和部署建议,感兴趣的小伙... 目录什么是 ReFlex?为什么选择 Reflex?安装与环境配置构建你的第一个应用核心概念解析组件

C#通过进程调用外部应用的实现示例

《C#通过进程调用外部应用的实现示例》本文主要介绍了C#通过进程调用外部应用的实现示例,以WINFORM应用程序为例,在C#应用程序中调用PYTHON程序,具有一定的参考价值,感兴趣的可以了解一下... 目录窗口程序类进程信息类 系统设置类 以WINFORM应用程序为例,在C#应用程序中调用python程序