原文地址:https://mecid.github.io/2019/06/12/understanding-property-wrappers-in-swiftui/

@States

通过使用 @State 修饰器我们可以关联出 View 的状态. SwiftUI 将会把使用过 @State 修饰器的属性存储到一个特殊的内存区域,并且这个区域和 View struct 是隔离的. 当 @State 装饰过的属性发生了变化,SwiftUI 会根据新的属性值重新创建视图

struct ProductsView: View {
let products: [Product] @State private var showFavorited: Bool = false var body: some View {
List {
Button(
action: { self.showFavorited.toggle() },
label: { Text("Change filter") }
) ForEach(products) { product in
if !self.showFavorited || product.isFavorited {
Text(product.title)
}
}
}
}
}

这个例子里我们创建了一个列表,点击按钮 showFavorited 会发生值的取反操作,然后 SwiftUI 会通过最新的值更新值

译者:这个 demo 在最新的 xcode 11 beta 6 中已经无法运行起来了,因为 Button 组件的语法已经修改了

@Binding

有时候我们会把一个视图的属性传至子节点中,但是又不能直接的传递给子节点,因为在 Swift 中值的传递形式是值类型传递方式,也就是传递给子节点的是一个拷贝过的值。但是通过 @Binding 修饰器修饰后,属性变成了一个引用类型,传递变成了引用传递,这样父子视图的状态就能关联起来了。

struct FilterView: View {
@Binding var showFavorited: Bool var body: some View {
Toggle(isOn: $showFavorited) {
Text("Change filter")
}
}
} struct ProductsView: View {
let products: [Product] @State private var showFavorited: Bool = false var body: some View {
List {
FilterView(showFavorited: $showFavorited) ForEach(products) { product in
if !self.showFavorited || product.isFavorited {
Text(product.title)
}
}
}
}
}

我们在 FilterView 视图里用 @Binding 修饰 showFavorited 属性, 在传递属性是使用 $ 来传递 showFavorited 属性的引用,这样 FilterView 视图就能读写父视图 ProductsView 里的状态值了,并且值发生了修改 SwiftUI 会更新 ProductsView 和 FilterView 视图

译者:在 FilterView 视图里,Toggle 组件的创建也使用 $showFavorited 这种格式,因为 Toggle 组件会修改传入的值,如果是一个纯读的组件比如 Text 就不需要 使用 $showFavorited, 直接 Text(showFavorited) 使用就好了

@ObservedObject

@ObservedObject 的用处和 @State 非常相似,从名字看来它是来修饰一个对象的,这个对象可以给多个独立的 View 使用。如果你用 @ObservedObject 来修饰一个对象,那么那个对象必须要实现 ObservableObject 协议,然后用 @Published 修饰对象里属性,表示这个属性是需要被 SwiftUI 监听的

final class PodcastPlayer: ObservableObject {
@Published private(set) var isPlaying: Bool = false func play() {
isPlaying = true
} func pause() {
isPlaying = false
}
}

我们定义了一个 PodcastPlayer 类,这个类可以给不同的 View 使用,SwiftUI 会追踪使用 View 里经过 @ObservableObject 修饰过的对象里进过 @Published 修饰的属性变换,一旦发生了变换,SwiftUI 会更新相关联的 UI

struct EpisodesView: View {
@ObservedObject var player: PodcastPlayer
let episodes: [Episode] var body: some View {
List {
Button(
action: {
if self.player.isPlaying {
self.player.pause()
} else {
self.player.play()
}
}, label: {
Text(player.isPlaying ? "Pause": "Play")
}
)
ForEach(episodes) { episode in
Text(episode.title)
}
}
}
}

译者:这个 demo 在最新的 xcode 11 beta 6 中已经无法运行起来了,因为 Button 组件的语法已经修改了

@EnvironmentObject

从名字上可以看出,这个修饰器是针对全局环境的。通过它,我们可以避免在初始 View 时创建 ObservableObject, 而是从环境中获取 ObservableObject

SceneDelegate.swift 文件

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let window = UIWindow(frame: UIScreen.main.bounds)
let episodes = [
Episode(id: 1, title: "First episode"),
Episode(id: 2, title: "Second episode")
] let player = PodcastPlayer()
window.rootViewController = UIHostingController(
rootView: EpisodesView(episodes: episodes)
.environmentObject(player)
)
self.window = window
window.makeKeyAndVisible()
}
}

EpisodesView.swift 文件

struct EpisodesView: View {
@EnvironmentObject var player: PodcastPlayer
let episodes: [Episode] var body: some View {
List {
Button(
action: {
if self.player.isPlaying {
self.player.pause()
} else {
self.player.play()
}
}, label: {
Text(player.isPlaying ? "Pause": "Play")
}
)
ForEach(episodes) { episode in
Text(episode.title)
}
}
}
}

可以看出我们获取 PodcastPlayer 这个 ObservableObject 是通过 @EnvironmentObject 修饰器,但是在入口需要传入 .environmentObject(player) 。@EnvironmentObject 的工作方式是在 Environment 查找 PodcastPlayer 实例。

@Environment

继续上面一段的说明,我们的确开一个从 Environment 拿到用户自定义的 object,但是 SwiftUI 本身就有很多系统级别的设定,我们开一个通过 @Environment 来获取到它们

struct CalendarView: View {
@Environment(\.calendar) var calendar: Calendar
@Environment(\.locale) var locale: Locale
@Environment(\.colorScheme) var colorScheme: ColorScheme var body: some View {
return Text(locale.identifier)
}
}

通过 @Environment 修饰的属性,我们开一个监听系统级别信息的变换,这个例子里一旦 Calendar, Locale, ColorScheme 发生了变换,我们定义的 CalendarView 就会刷新

谢谢

[译]理解 SwiftUI 里的属性装饰器@State, @Binding, @ObservedObject, @EnvironmentObject的更多相关文章

  1. typescript装饰器定义 类装饰器 属性装饰器 装饰器工厂

    /* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...

  2. @property属性装饰器

    顾名思义,@property就是一个跟属性相关的装饰器, 使用了它之后,取值和赋值操作都变得简洁 from datetime import date, datetime class User: def ...

  3. Pytest里,mark装饰器的使用,双引号,没引号,这种差别很重要

    按最新版的pytest测试框架. 如果只是单一的mark,不要加任何引号. 如果是要作and ,not之类的先把,一定要是双引号! 这个要记清楚,好像和以前版本的书上介绍的不一样,切记! import ...

  4. SwiftUI 中一些和响应式状态有关的属性包装器的用途

    SwiftUI 借鉴了 React 等 UI 框架的概念,通过 state 的变化,对 View 进行响应式的渲染.主要通过 @State, @StateObject, @ObservedObject ...

  5. TypeScript学习笔记(九):装饰器(Decorators)

    装饰器简介 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式. 需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变. 若要启用实验性的装饰器特 ...

  6. http://python.jobbole.com/85056/ 简单 12 步理解 Python 装饰器,https://www.cnblogs.com/deeper/p/7482958.html另一篇文章

    好吧,我标题党了.作为 Python 教师,我发现理解装饰器是学生们从接触后就一直纠结的问题.那是因为装饰器确实难以理解!想弄明白装饰器,需要理解一些函数式编程概念,并且要对Python中函数定义和函 ...

  7. 理解 Python 装饰器看这一篇就够了

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...

  8. day20-Python运维开发基础(装饰器 / 类中的方法 / 类的方法变属性)

    1. 装饰器 / 类中的方法 / 类的方法变属性 # ### 装饰器 """ 定义:装饰器用于拓展原来函数功能的一种语法,返回新函数替换旧函数 优点:在不更改原函数代码的 ...

  9. python装饰器理解

    1.装饰器的作用 在不修改被装饰对象的源代码以及调用方式的前提下为被装饰对象添加新功能 原则: 1.不修改被装饰对象的源代码2.不修改被装饰对象的调用方式 目标: 为被装饰对象添加新功能 2.装饰器的 ...

随机推荐

  1. java获取当前时间的年周月季度等的开始结束时间

    import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * Created b ...

  2. 你肯定不知道的oracle数据库和sql server的这些区别

    它们两者之间的区别主要体现在六大方面: 一是开放性. 1.SQL Server 只可在windows上运行,缺乏开放性,操作系统的稳定对数据库是非常重要的. Windows9X系列产品比较偏重于桌面应 ...

  3. Mobile的HTML5网页内快速滚动和回弹的效果

    style="overflow: auto;-webkit-overflow-scrolling: touch; 这个可以让页面在Native端滚动时模拟原生的弹性滚动效果 下面是微信浏览器 ...

  4. SPOJ 2798 QTREE3 - Query on a tree again!

    原oj题面 Time limit 2000 ms Memory limit 1572864 kB Code length Limit 50000 B OS Linux Language limit A ...

  5. 使用ajax异步下载文件,后端为struts2

    前端采用伪表单: 然后调用 后台代码为 效果图:

  6. uniapp开发

    问题1:去掉导航条 给一个单独的页面隐藏顶部的 导航栏 可以在pages.json里 单独的路由style下面配置  "app-plus": {"titleNView&q ...

  7. SpringBoot:配置文件及自动配置原理

    西部开源-秦疆老师:基于SpringBoot 2.1.6 的博客教程 秦老师交流Q群号: 664386224 未授权禁止转载!编辑不易 , 转发请注明出处!防君子不防小人,共勉! SpringBoot ...

  8. (转)CBC模式和ECB模式解读

    一 什么是CBC模式 CBC模式的全称是Cipher Block Chaining模式(密文分组链接模式),之所以叫这个名字,是因为密文分组像链条一样相互连接在一起. 在CBC模式中,首先将明文分组与 ...

  9. 基础:高通bring up camera【转】

    本文转载自:http://blog.csdn.net/liwei16611/article/details/53056710 bring UP 需要做的工作   1.kernelspace senso ...

  10. 《图解 CSS3 核心技术与案例实战》

    第一章 解开 CSS3 的面纱 使用 CSS3 的好处 减少开发和维护成本:如传统实现圆角边框需要绘图.切图才能完成,而使用 css 可以直接完成 提高页面性能 渐进增强(Progressive En ...