这是我参与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自己决定的。
最终效果如下: