Developing Applications for iOS using SwiftUI
Total Lecture: 15
current: [4-6]
Applying MVVM
statics vars and funcs
.foregroundColor(.orange)
完全写法.foregroundColor(Color.orange)
Reactive UI : ObservableObject
ObervableObject
协议中有一个var objectWillChange: ObservableObjectPublisher
当你想要表示某些内容发生更改时向其发送函数send()
表示这个对象将会发生改变。
1 | class EmojiMemoryGame: ObservedObject { |
Reactive UI : @Published
1 | class EmojiMemoryGame: ObservedObject { |
将@Published
放在变量上,那么如果该变量发生更改,它会显示某些内容已更改
@Published意味着做出改变
RectiveUI : @ObservedObject
1 | struct EmojiMemoryGameView: View { |
@ObservedObject
意思是如果这个东西表示某些内容发生了变化,请重新绘制我
- 注意
@ObservedObject
必须被标记为事实,状态,它表明正在观察这件事,所以永远不要在那里说等于,如果你确实需要这么做,可以使用@StateObject
1 | struct EmojiMemoryGameView: View { |
它仅位于此视图内,无法与其他视图共享此ViewModel
,如果用@StateObject
,那么它就是一个对象,因为它是您视图中的一个实例
@ObservedObject VS @StateObject
生命周期
1 | struct EmojiMemoryGameView: View { |
此时viewModel
的生命周期与UI中的VIew
存在的生命周期相关。视图出现在屏幕上时,该变量就会存在,一旦试图不再位于其他视图的body
中,它就会被释放
@ObservedObject
像var
一样,只是关注变量的变化,这个变量是你假设有人会传递给你变量,生命周期由外部传递者决定
作用域
@StateObject
只能在此视图中或在它body
中创建的其他视图,或级联视图的body
中,不能用于同级视图中
Protocols,enum,Optional
ForEach identity
为什么动画效果是渐进渐出,而不是在屏幕移动?
1 | var cards: some View { |
洗牌时,我们将其中一张牌从数字7移动到数字0,而另一张从0到4,但从ForEach
的角度来看,它仍然显示索引为0的卡片,它恰好从下面改变了,但它仍然显示这张0卡,这就是为什么它在移入那里中逐渐消失的原因。
id: \.self
id: \.self
的意思是我是ForEach
,我正在尝试识别(identity
)这里的每一件事。它是一个范围(viewModel.cards.indices
),但我正在尝试识别他们中的每一个,以便我可以将其连接到此视图。我该用什么来识别这些东西?**id: \.self
表示使用事物本身**,这对于Int
来说非常有效,比如0,1,2,3
之所以说“可哈希”,是因为它需要能够对这个东西进行哈希处理,因为它将在每个视图和这个视图之间构建一个小哈希表,一个小字典
我们希望ForEach
不是针对卡片的索引,而是针对卡片本身,因此卡片在数组中移动时,视图也会移动。
1 | var cards: some View { |
Enum
当我们有一些值是离散的数据类型时,都会使用枚举。它没有存储变量,有计算属性,枚举的值是离散的,是值类型,强大的地方在于,可以将数据与每个case
关联起来。
FIXME: bogus!
Layout @ViewBuilder
Layout
屏幕上的空间如何分配给所有出现在那里的视图
- 为视图提供一定的空间,
ContentView(应用程序顶部的内容)
提供整个屏幕,这是起点 - 视图在提供空间后,唯一可以选择视图大小(size)的是视图本身,它们根据提供给它们的空间选择大小
- 视图位置(position)由容器确定
HStack and VStack
1 | HStack { |
这里有一个HStack
,它有两个文本和文本之间的图像,当HStack
去布局这些内容时,它首先会提供空间给Import
,Import
会获得绘制完整单词所需的所有空间,然后是Image
获得空间,最后是Unimportant
,如果没有剩余空间,它会显示Unim...
firstTextBaseline
1 | HStack(alignment: .firstTextBaseline){} |
如果它们有一堆文本,也许文本的大小不完全相同,大字体,小字体,你希望它们的基线全部对齐
LazyHStack and LazyVStack
Lazy意味着它实际上不会布局不在屏幕上的视图 ,它们不会占用它们内部的所有空间,即使它们内部是灵活的视图,这点与VStack``HStack
不同,如果它们有一个灵活的视图,它们会使用额外的空间
LazyHGrid and LazyVGrid
它的大小会根据其内部的内容而调整,因此您通常将其放置在ScrollView
中
Grid
用于在两个方向上布局内容的视图
ScrollView
占用了提供给它的所有空间,它会将内容放入其中,并让你在其上滚动
VeiwThatFits
它的ViewBuilder
有一个视图列表,它要做的是检查所有视图大小,以提供给ViewThatFits
会选择最适合的一个
ZStack
ZStack
的大小,它将是子视图需要适合的的最大大小,如果最大的视图是完全灵活的,那么ZStack
将是完全灵活的,并占用所有提供给它的空间。
ZStack
sizes itself to fit its childrenif even one of its children is fully flexible size, then the ZStack wlll be too
.background modifier
1 | Text("hello").backgound(Rectangle().foregroundColor(.red)) |
sized to the Text
.overlay modifier
1 | Circle().overlay(Text("Hello"), alignment: .center) |
sized to the Circle
谁负责
ZStack
的尺寸,它始终是堆栈修饰符中最大的那个
Modifiers
.aspectRatio
GeometryReader
它是一个视图,它占用提供给它的所有空间,查看它的大小,然后将其传递到其子视图
1 | var body: View { |
geometry
参数是GeometryProxy
类型
1 | struct GeometryProxy { |
Safe Area
1 | ZStack { ... }.edgesIgnoreingSafeArea([.top]) |
Demo
1 | var cards: some View { |
.flexible()
是GridItem()
的默认参数
@ViewBuilder
func
或 read-only computed var
可以被标记为@ViewBuilder
,被标记后,它们的内容被解释为视图列表
func
1 |
|
The above would return a
TupleView<RoundedRectangle,RoundedRectangle,Text>
范型最多只有11个不在乎类型
applying in param
当你将内容传递给ZStack
或HStack
时,它是一个ViewBuilder
,它是如何在init
中做到这一点的?它们将该参数标记为@ViewBuilder
That argument’s type must be “a function that returns a View”
1 | struct AspectVGrid<Item: Identifiable, ItemView: View>: View { |