这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
前文
- SwiftUI实战-仿写微信App(一)
- SwiftUI实战-仿写微信App(二)
- SwiftUI实战-仿写微信App(三)
- SwiftUI实战-仿写微信App(四)
- SwiftUI实战-仿写微信App(五)
一、跨层级数据共享
前面的章节提到过,父级使用@State
修饰变量,子级使用@Binding
修饰变量,并通过参数的传递,可以实现变量的共享即不管是父级还是子级改变该变量的值,对方都会感知到。
graph LR
父级 --> |共享|变量
子级 --> |共享|变量
但是还有一种情况,就是数据在多个View
中都有引用,它们并不是父子级的关系,也希望共享该变量。
graph TD
View1 ==> |共享|变量
View3 ==> |共享|变量
View2.1 --> View3
View2.2 --> View3
View1 --> View2.1
View1 --> View2.2
View1和View3都持有该变量,但是并非父子级关系。
二、Combine框架
SwiftUI
提供了一个框架Combine
,使用MVVM
设计模式。
MVVM
是Model-View-ViewModel
的简写。
Model
指代共享变量。View
就是视图层,同上文的View1,View2,View3
。ViewModel
类似一个枢纽,把Model
和View
联结起来。
graph LR
subgraph ViewModel
Model
end
subgraph View
ViewModel
end
ViewModel
持有变量Model
,通过某些手段(后面会讲)将Model
变成共享变量。View
中引入ViewModel
类,通过ViewModel
操作Model
,比如增删改等操作,其它引用View
都会得到通知。
三、ModelView
先创建一个ModelView
类。Xcode
创建Swift File
而非SwiftUI View
import Foundation
class AnimalViewModel{
}
注意:这里
ViewModel
必须是class
,而不是struct
。
然后实现ObservableObject
协议,通知SwiftUI
这是一个ViewModel
类。
四、Model
接下来在ViewModel
中创建Model
。
class AnimalViewModel: ObservableObject{
var animals: [String] = []
}
Model
不拘泥数据类型,不管是数组,还是其它类型均可,即使是一个Bool
变量都可以。
同理Model
也需要证明自己,所以使用@Publish
注解修饰。
@Published var animals: [String] = []
五、View
在View
中引用ViewModel
。
创建两个View
:TVView
和PCView
,分别引入ViewModel
,并显示同样的List
列表。
比如TVView
,PCView
同TVView
。
struct TVView: View {
var model: AnimalViewModel
var body: some View {
List{
Text("TVView")
Button(action: {
model.animals.append("cat")
}, label: {
Text("addAnimal")
})
ForEach(0..<model.animals.count, id: \.self){index in
Text(model.animals[index])
}
}
}
}
同前面两步,View
同样需要通知SwiftUI
它对ViewModel
的引用。使用@EnvironmentObject
修饰ViewModel
。
@EnvironmentObject var model: AnimalViewModel
六、ViewModel
层级
最后,虽然使用@EnvironmentObject
注解标记了ViewModel
,但是没有通知SwiftUI
在哪创建ViewModel
,也即在哪个View
层级上创建ViewModel
。
graph TD
subgraph View1上创建ViewModel
View1 --> View2.1
View1 --> View2.2
subgraph View2.1上创建ViewModel
View2.1 --> View3.1
View2.1 --> View3.2
end
View2.2 --> View3.3
View2.2 --> View3.4
end
如上图,
如果在View1
层级创建ViewModel
,Model
能够在View1
、View2
和View3
所有层级中共享。
如果在View2.1
层级创建,Model
只能在View2.1
、View3.1
和View3.2
中共享。
那么具体怎么创建呢?
在WeChatModel
项目中,ContentView
作为最上级的View
,只需要找到创建ContentView
的位置。
如下图
看清楚,虽然都是EnvironmentObject
,一个是使用@EnvironmentObject
注解标记View
中的ViewModel
,另一个是指定ViewModel
的影响范围。
我们只需要指定影响范围即可,具体什么时间创建,是由
SwiftUI
自己决定的。
最终效果如下: