SwiftUI:使用 @EnvironmentObject 从环境中读取自定义值
SwiftUI的环境使我们可以使用来自外部的值,这对于读取Core Data上下文或视图的展示模式等很有用。但是我们也可以将自定义对象发送到环境中,并在以后将它们读出来,这使我们可以在复杂的应用程序中更轻松地共享数据。
您已经了解了如何使用@State
处理单个视图的局部状态,以及@ObservedObject
如何使我们在视图之间传递一个对象,以便我们可以共享它。好吧,@ EnvironmentObject
更进一步:我们可以将对象放置到环境中,以便任何子视图都可以自动访问它。
假设我们在一个应用程序中有多个视图,所有视图都排成一排:视图A显示视图B,视图B显示视图C,C显示D,D显示E。视图A和E都希望访问同一对象,但是要从A到达E,您需要经过B,C和D,而他们并不关心该对象。如果我们使用@ObservedObject
,则需要将我们的对象从每个视图传递到下一个视图,直到它最终到达可以使用该视图的视图E,这很烦人,因为B,C和D不在乎它。使用@EnvironmentObject
,视图A可以将对象放入环境中,视图E可以从环境中读取对象,而视图B,C和D不必知道发生了什么。
环境对象的一个复杂性是其子对象的构成,因为视图可以访问的环境对象取决于其父视图。例如,如果视图A可以访问环境对象,而视图B在视图A的内部——即视图B放在A的body
属性中——那么视图B也可以访问该环境对象。这意味着,如果视图A是导航视图,则所有压入导航堆栈的视图都可以访问同一环境。但是,如果视图A以工作表(sheet)的形式显示视图B,则它们不会自动共享环境数据,因此我们需要手动发送。Apple已将此工作表情况描述为他们想要修复的错误,因此我希望在以后对SwiftUI的更新中会有所改变。
在向您展示一些代码之前,还有最后一件事:环境对象使用您已经学过的ObservableObject
协议,SwiftUI将自动确保共享同一环境对象的所有视图在更改时都会更新。
好的,让我们看一些代码,这些代码展示了如何使用环境对象在两个视图之间共享数据。首先,这是我们可以使用的一些基本数据:
class User: ObservableObject {
@Published var name = "Taylor Swift"
}
如您所见,使用ObservableObject
和@Published
就像我们以前学到的那样——您积累的所有知识将继续得到回报。
接下来,我们可以定义两个SwiftUI视图以使用我们的新类。这些将使用@EnvironmentObject
属性包装器来表示此数据的值来自环境,而不是在本地创建:
struct EditView: View {
@EnvironmentObject var user: User
var body: some View {
TextField("Name", text: $user.name)
}
}
struct DisplayView: View {
@EnvironmentObject var user: User
var body: some View {
Text(user.name)
}
}
该@EnvironmentObject
属性包装器将自动在环境中查找User
实例,并将其找到的内容放入user
属性中。如果在环境中找不到用户,您的代码就会
,因此请小心。
现在,我们可以将这两个视图放在一个地方,并发送一个User
实例供它们使用:
struct ContentView: View {
let user = User()
var body: some View {
VStack {
EditView().environmentObject(user)
DisplayView().environmentObject(user)
}
}
}
这就是使我们的代码正常工作所要做的一切——您现在就可以运行该应用并更改文本字段,以查看其值显示在下面的文本视图中。当然,我们可以在单个视图中表示出来,但是通过这种方式,您可以确切地看到使用环境对象时通信的无缝性。
现在,这是最聪明的部分。尝试将ContentView
的body
属性重写为:
VStack {
EditView()
DisplayView()
}
.environmentObject(user)
您会发现它的表现完全相同。现在,我们将用户置于ContentView
环境中,但是由于EditView
和DisplayView
都是ContentView
的子级,因此它们会自动继承其环境。
现在,您可能想知道SwiftUI如何在.environmentObject(user)
和@EnvironmentObject var user: User
之间建立连接——如何知道将该对象放入正确的属性?
好吧,您已经了解到字典如何让我们使用一种类型作为键key
,而另一种类型作为值。环境有效地使我们可以将数据类型本身用作键,并将类型的实例用作值。刚开始时,这有点令人费解,但可以这样想象:键是Int
,String
和Bool
之类的,值是5,“ Hello”和 true,这意味着我们可以说“给我Int
”,我们将得到5。
译自 Reading custom values from the environment with @EnvironmentObject
- Druid-目前最好的连接池
- 【算法】哈希表的诞生
- java中获得文件大小代码
- 【Java】泛型学习笔记
- 读写文件具体操作
- 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)
- ERROR 1396 (HY000): Operation CREATE USER faile...
- hive安装后测试
- 【算法】论平衡二叉树(AVL)的正确种植方法
- 【JavaScript】 JS面向对象的模式与实践
- log4j:ERROR Failed to load driver
- 前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例
- 前端MVC Vue2学习总结(六)——axios与跨域HTTP请求、Lodash工具库
- spring之config.xml完整版示例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 消息未读之点不完的小红点(Node+Websocket)
- pkg版本规范管理自动化最佳实践
- 数据库驱动问题:MySQLNonTransientConnectionException
- 喜大普奔,es2019登场
- [译] Object.assign 和 Object Spread 之争, 用谁?
- 急速 debug 实战二(浏览器 - 调试线上篇)
- via the 'serverTimezone' configuration property
- 急速 debug 实战一(浏览器-基础篇)
- MongoDB系列一: Replica Set 集群搭建实战
- 函数式编程看React Hooks(一)简单React Hooks实现
- 函数式编程看React Hooks(二)事件绑定副作用深度剖析
- Vue 开发必须知道的 36 个技巧【近1W字】
- 吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧《上》
- 【漫游Github】无编译/无服务器,实现浏览器的 CommonJS 模块化
- 《秋风日常第一期》白板协作工具 LeanBoard