学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

Stanford CS193p - [4-6]

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
2
3
4
5
6
7
8
9
10
class EmojiMemoryGame: ObservedObject {

private var model = createMemoryGame()
var objectWillChange: ObservableObjectPublisher

func shuffle(){
model.shuffle()
objectWillChange.send()
}
}

Reactive UI : @Published

1
2
3
4
5
6
7
8
9
10
class EmojiMemoryGame: ObservedObject {

@Published private var model = createMemoryGame()
// var objectWillChange: ObservableObjectPublisher

func shuffle(){
model.shuffle()
objectWillChange.send()
}
}

将@Published放在变量上,那么如果该变量发生更改,它会显示某些内容已更改

@Published意味着做出改变

RectiveUI : @ObservedObject

1
2
3
struct EmojiMemoryGameView: View {
@ObservedObject var viewModel: EmojiMemoryGame = EmojiMemoryGame()
}

@ObservedObject 意思是如果这个东西表示某些内容发生了变化,请重新绘制我

  • 注意
    @ObservedObject必须被标记为事实,状态,它表明正在观察这件事,所以永远不要在那里说等于,如果你确实需要这么做,可以使用@StateObject
1
2
3
struct EmojiMemoryGameView: View {
@StateObject var viewModel: EmojiMemoryGame = EmojiMemoryGame()
}

它仅位于此视图内,无法与其他视图共享此ViewModel,如果用@StateObject,那么它就是一个对象,因为它是您视图中的一个实例

@ObservedObject VS @StateObject

生命周期

1
2
3
struct EmojiMemoryGameView: View {
@StateObject var viewModel: EmojiMemoryGame = EmojiMemoryGame()
}

此时viewModel 的生命周期与UI中的VIew存在的生命周期相关。视图出现在屏幕上时,该变量就会存在,一旦试图不再位于其他视图的body中,它就会被释放

@ObservedObjectvar一样,只是关注变量的变化,这个变量是你假设有人会传递给你变量,生命周期由外部传递者决定

作用域

@StateObject只能在此视图中或在它body中创建的其他视图,或级联视图的body中,不能用于同级视图中

Protocols,enum,Optional

ForEach identity

为什么动画效果是渐进渐出,而不是在屏幕移动?

1
2
3
4
5
6
7
8
9
10
var cards: some View {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 85),spacing: 0)],spacing: 0) {
ForEach(viewModel.cards.indices,id: \.self) { index in
CardView(viewModel.cards[index])
.aspectRatio(2/3, contentMode: .fit)
.padding(4)
}
}
.foregroundColor(.orange)
}

洗牌时,我们将其中一张牌从数字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
2
3
4
5
6
7
8
9
10
11
12
13
14
var cards: some View {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 85),spacing: 0)],spacing: 0) {
ForEach(viewModel.cards) { card in
VStack(spacing:0){
CardView(card)
.aspectRatio(2/3, contentMode: .fit)
.padding(4)
Text(card.id)
}

}
}
.foregroundColor(.orange)
}

Enum

当我们有一些值是离散的数据类型时,都会使用枚举。它没有存储变量,有计算属性,枚举的值是离散的,是值类型,强大的地方在于,可以将数据与每个case关联起来。

FIXME: bogus!

Layout @ViewBuilder

Layout

屏幕上的空间如何分配给所有出现在那里的视图

  1. 为视图提供一定的空间,ContentView(应用程序顶部的内容)提供整个屏幕,这是起点
  2. 视图在提供空间后,唯一可以选择视图大小(size)的是视图本身,它们根据提供给它们的空间选择大小
  3. 视图位置(position)由容器确定

HStack and VStack

1
2
3
4
5
HStack {
Text("Import").layoutPriority(100) // any floating point number is okay
Image(systemName:"arrow.up"). // the default layout priority is 0
Text("Unimportant")
}

这里有一个HStack,它有两个文本和文本之间的图像,当HStack去布局这些内容时,它首先会提供空间给ImportImport会获得绘制完整单词所需的所有空间,然后是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将是完全灵活的,并占用所有提供给它的空间。

ZStacksizes itself to fit its children

if 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
2
3
4
5
var body: View {
GeometryReader { geometry in //using trailing closure syntax for content: parameter
...
}
}

geometry参数是GeometryProxy类型

1
2
3
4
5
struct GeometryProxy {
var size: CGSize
func frame(in: CoordinateSpace) -> CGRect
var safeAreaInsets: EdgeInsets
}

Safe Area

1
ZStack { ... }.edgesIgnoreingSafeArea([.top])

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var cards: some View {
LazyVGrid(columns: [GridItem(.flexible()),spacing: 0)],spacing: 0) {
ForEach(viewModel.cards) { card in

CardView(card)
.aspectRatio(2/3, contentMode: .fit)
.padding(4)
.onTapGesture {
viewModel.choose(card)
}
}
}
.foregroundColor(.orange)
}

.flexible()GridItem()的默认参数

@ViewBuilder

funcread-only computed var可以被标记为@ViewBuilder,被标记后,它们的内容被解释为视图列表

func

1
2
3
4
5
6
7
@ViewBuilder
func front(of card: Card) -> some View {
let shape = RoundedRectangle(cornerRadius: 20)
shape.fill(.white)
shape.stroke()
Text(card.content)
}

The above would return a TupleView<RoundedRectangle,RoundedRectangle,Text>

范型最多只有11个不在乎类型

applying in param

当你将内容传递给ZStackHStack时,它是一个ViewBuilder,它是如何在init中做到这一点的?它们将该参数标记为@ViewBuilder

That argument’s type must be “a function that returns a View”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct AspectVGrid<Item: Identifiable, ItemView: View>: View {
var items: [Item]
var aspectRatio: CGFloat = 1
var content: (Item) -> ItemView

init(_ items: [Item], aspectRatio: CGFloat, @ViewBuilder content: @escaping (Item) -> ItemView) {
self.items = items
self.aspectRatio = aspectRatio
self.content = content
}

var body: some View {
GeometryReader { geometry in
let gridItemSize = gridItemWidthThatFits(count: items.count, size: geometry.size, atAspectRatio: aspectRatio)


LazyVGrid(columns: [GridItem(.adaptive(minimum: gridItemSize),spacing: 0)],spacing: 0) {
ForEach(items) { item in
content(item)
.aspectRatio(aspectRatio, contentMode: .fit)

}
}
}
}
}