SwiftUI:何时使用@State与@Published

k97glaaz  于 2022-11-21  发布在  Swift
关注(0)|答案(2)|浏览(278)

我觉得这是一个已经在某个地方被问到的问题,但我找不到太多关于它的信息。
当使用变量来更新UI时,我们何时/为什么要在视图中使用@State,而不是在ViewModel中使用@Published
这是在我试图掌握MVVM体系结构的背景下。我一般理解其中的区别,只是不是当涉及到两者都可以用同样的方式轻松完成的事情时。
下面,我有两个例子做同样的事情,但是一个使用@State,而另一个使用@Published和一个ViewModel。
@State示例:

struct MyView: View {
    @State var backgroundIsRed = false

    var body: some View {
        ZStack {
            if backgroundIsRed {
                Color.red
            } else {
                Color.green
            }
        }
        .onTapGesture { backgroundIsRed.toggle() }

    }
}

@Published示例:

class ViewModel: ObservableObject {
    @Published var backgroundIsRed = false
}

struct MyView: View {

    @StateObject var viewModel = ViewModel()

    var body: some View {
        ZStack {
            if viewModel.backgroundIsRed {
                Color.red
            } else {
                Color.green
            }
        }
        .onTapGesture { viewModel.backgroundIsRed.toggle() }
    }
}
4uqofj5v

4uqofj5v1#

例如,我认为“Published”方法可以帮助您为VM创建测试结构。
以您的示例为例,您可以创建一个协议:

protocol ViewModelProtocol {
   var backgroundIsRed: Bool { get }
   var date: Date { get } // Created for example purposes
}

然后道:

class ViewModel: ViewModelProtocol, ObservableObject {
    @Published var backgroundIsRed = false
    @Published var date = Date()
}

class ViewModelMock: ViewModelProtocol, ObservableObject {
    @Published var backgroundIsRed = true
    @Published var date = Mock.Date
}

struct MyView: View {
    @StateObject var viewModel: ViewModelProtocol = ViewModel()
    //@StateObject var viewModel: ViewModelProtocol = ViewModelMock()

    var body: some View {
        ZStack {
            if viewModel.backgroundIsRed {
                Color.red
            } else {
                Color.green
            }
        }
        .onTapGesture { viewModel.backgroundIsRed.toggle() }
    }
}

另一方面,State方法提供了一种实现逻辑的更直接的方法。
除此之外,没有任何特别的理由认为一个比另一个更好。希望这能帮助你选择在每种情况下应该使用哪一个。

s2j5cfk0

s2j5cfk02#

我们在SwiftUI中不需要MVVM,因为View结构体已经是视图模型了,也就是说,它保存了SwiftUI用来创建/更新/删除屏幕上实际UIView对象的数据。如果使用实际对象来完成这项工作,那么你就添加了不必要的间接层,并且会得到SwiftUI巧妙使用值语义所要消除的那种一致性错误。
最好把@StateObject看作@State,但如果你需要一个引用类型,比如你想异步加载/保存/同步数据。现在我们有了功能更强大的.task修饰符,这种情况并不常见。
如果你的目标是将相关的变量组合在一起,并拥有可测试的逻辑,那么只需使用带有自定义结构体的@State var,这比正确实现@StateObject要简单得多。然而,你必须首先学习用于你的逻辑的mutating func。同样,如果你想在自定义结构体中访问@Environment,你需要学习DynamicProperty

相关问题