Section 12 : Binding and Screen Size - HH-Ge/BuildAnAppWithSwiftUI GitHub Wiki
Bind states across multiple views and detect the screen size.
在多个视图之间绑定数据并检测屏幕尺寸。
(1)抽取子视图
按住⌘点击显示头像的按钮,在快捷菜单中选择 Extract Subview
,输入名称AvatarView
后回车,再次按住 ⌘点击,在快捷菜单中选择 Jump to Definition
,光标跳转到抽取出来的子视图定义。
(2)设置绑定
在子视图中,会看到一条报错信息。显然这是因为子视图中使用的 showProfile 变量少了定义。像设定状态那样,可这次不是用@State
,而是用@Binding
设定一个变量 showProfile,声明好类型。这样那条报错信息没有了。记得不要有初始值!!
@Binding var showProfile: Bool
(3)使用绑定
这里是没错误了,但是上面调用子视图的地方又报错了,说少了调用需要的参数。对,那个参数就是刚才在子视图里面设置的。输入括号,就会看见自动提示。选择后输入参数 showProfile(这个 showProfile 可是主视图中的状态变量哈),系统提示应该在变量前面加上$
,嗯嗯,我们听话,加上就是。
AvatarView(showProfile: $showProfile) // 状态变量绑定到子视图的参数
这个时候预览一下,可以看见一切和昨天结束的时候一样。这是因为状态 showProfile 的值先通过绑定传给了子视图,子视图的按钮点击又通过绑定影响了主视图的状态。由此可见,数据是双向绑定的。这看起来好像和 Vue 的差不多,起码词儿是一样的。
(1)将 VStack 的内容抽取到新的文件
先创建一个新的 SwiftUI View 文件,文件名为 HomeView,然后将 Home 文件中 VStack 的部分(不包括修饰器)剪切,输入HomeView()
。再到新建的 HomeView 文件中,将默认的文本 Text 删掉,粘贴上刚才剪切的代码。
(2)设置绑定
可以看见,刚才的错误再次出现了,这里也需要设定一个绑定来传递状态。照猫画虎写就行了。(我觉得这是课程故意安排的 创建绑定过程练习,而且还能体会到绑定的不同场景。)
(3)使用绑定
那同样的,主视图调用的时候也需要向刚才那样加上绑定状态。
应该万事大吉了。预览一下,咦?这是什么鬼?其实这里还有个问题没有解决。就是我们一直没有关注的部分。每个新建的 SwiftUI View 文件,都包含两个部分:视图
和预览
。前面一直在关注视图部分,对预览部分几乎没有理睬。现在就能看见预览部分报错了。这个是因为绑定的状态在预览中调用时没有值。所以加上值就好了。
HomeView(showProfile: .constant(false))
这里注意,直接输入 false
作为值是不行的。会报错:
Cannot convert value of type 'Bool' to expected argument type 'Binding<Bool>'
因为 false 的类型是 Bool
, 但是调用时需要的类型是 Binding<Bool>
。
(1)创建全局变量
在 Howe.swift 文件中的最下面,输入代码
let screen = UIScreen.main.bounds // 获得当前设备屏幕尺寸
使用 ⌥单击 screen,可以看见其类型是 CGRect
,一组描述位置和大小的数据。因为我们不会考虑改变这个 screen 的值,所以设成了常量。
另外,由于 screen 是设置在所有的 struct 之外,这就使其作用域成为全局。
(2)修改视图中有关的硬编码部分
.offset(y: showProfile ? 0 : screen.height) // showProfile 为 false 时隐藏,偏移整个屏幕高度,确保在屏幕之外
- 通过数据绑定可以在视图之间、文件之间同步状态(传递/修改状态值)
- 数据绑定使用 @Binding 关键字声明,要有类型,不能有默认值。
- 调用数据绑定的变量时要加上 $
- Home 侧重将多个视图放到一起,HomeView 侧重特定主屏幕的 UI
UIScreen.main.bounds
可以 获得当前设备屏幕尺寸,返回值类型是 CDRect