SwiftUI实战-仿写微信App(六)

1,472 阅读2分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

前文

一、跨层级数据共享

前面的章节提到过,父级使用@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设计模式。

MVVMModel-View-ViewModel的简写。

  • Model指代共享变量。
  • View就是视图层,同上文的View1,View2,View3
  • ViewModel类似一个枢纽,把ModelView联结起来。
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

image.png

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:TVViewPCView,分别引入ViewModel,并显示同样的List列表。

比如TVViewPCViewTVView

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层级创建ViewModelModel能够在View1View2View3所有层级中共享。

如果在View2.1层级创建,Model只能在View2.1View3.1View3.2中共享。

那么具体怎么创建呢?

WeChatModel项目中,ContentView作为最上级的View,只需要找到创建ContentView的位置。

如下图

image.png

看清楚,虽然都是EnvironmentObject,一个是使用@EnvironmentObject注解标记View中的ViewModel,另一个是指定ViewModel的影响范围。

我们只需要指定影响范围即可,具体什么时间创建,是由SwiftUI自己决定的。

最终效果如下:

屏幕录制2021-08-06 21.39.16.gif

附:代码地址

gitee.com/dkwingcn/we…