【问题标题】:SwiftUI Dependency InjectionSwiftUI 依赖注入
【发布时间】:2021-09-18 20:42:01
【问题描述】:

我有一个 SwiftUI 应用,它是一个基于标签的应用

struct Tab_View: View {
    var body: some View {
        TabView{
            Main1_View().tabItem {
                Text("Blah 1")
                Image("TabBar1")
            }
        
            Main2_View().tabItem {
                Text("Blah 2")
                Image("TabBar2")
            }
        }
    }
}

每个视图都有自己的视图控制器

struct Main1_View: View {

    @ObservedObject var viewModel: Main1_ViewModel = Main1_ViewModel()

    var body: some View {
        VStack(spacing:0){
           <<< VIEW CODE >>>
        }
    }
}

ViewModel 示例

class Main1_ViewModel: ObservableObject {

    @ObservableObject var settings: GameSettings

    func Randomise(){
        dataSource = settings.selectedFramework;
    }
}

GameSettings 类被多个视图模型使用,是一个 ObservableObject,在任何地方都是该类的相同实例。

我的背景是 C#,使用 CastleWindsor 进行依赖注入。

我的问题:是否有相当于传递 GameSettings 实例的 SwiftUI?

【问题讨论】:

    标签: dependency-injection swiftui


    【解决方案1】:

    由于您要求在另一个 ObservableObject(视图模型)中使用 ObservableObject (GameSettings)并且使用依赖注入,所以事情会有点复杂。

    为了将依赖注入的ObservableObject 注入到View,通常的解决方案是使用@EnvironmentObject。但是,您必须将对象从视图传递到其视图模型。在我的示例中,我在onAppear 中完成了该操作。副作用是对象是视图模型上的一个可选属性(您可以通过设置一个虚拟初始值来解决这个问题)。

    因为嵌套的ObservableObjects 不适用于@Published 类型(适用于值类型,而不是引用类型),所以您需要确保使用objectWillChange将 GameSettings 的任何更改传递给父视图模型,我使用 Combine 完成了。

    DispatchQueue.main.asyncAfter 部分只是为了表明当GameSettings 中的值更改时,视图确实实际上会更新。

    (请注意,我还更改了您的类型名称以使用 Swift 的骆驼大小写约定)

    import SwiftUI
    import Combine
    
    struct ContentView: View {
        @StateObject private var settings = GameSettings()
        
        var body: some View {
            TabView {
                Main1View().tabItem {
                    Text("Blah 1")
                    Image("TabBar1")
                }
            
                Main2View().tabItem {
                    Text("Blah 2")
                    Image("TabBar2")
                }
            }.environmentObject(settings)
        }
    }
    
    struct Main1View: View {
    
        @EnvironmentObject var settings: GameSettings
        @StateObject var viewModel: Main1ViewModel = Main1ViewModel()
    
        var body: some View {
            VStack(spacing:0){
                Text("Game settings: \(viewModel.settings?.myValue ?? "no value")")
            }.onAppear {
                viewModel.settings = settings
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    settings.myValue = "changed"
                }
            }
        }
    }
    
    class GameSettings : ObservableObject {
        @Published var myValue : String = "Test"
    }
    
    class Main1ViewModel: ObservableObject {
        private var cancellable : AnyCancellable?
        var settings: GameSettings? {
            didSet {
                print("Running init on Main1ViewModel")
                self.objectWillChange.send()
                cancellable = settings?.objectWillChange.sink(receiveValue: { _ in
                    print("Sending...")
                    self.objectWillChange.send()
                })
            }
        }
    }
    
    struct Main2View : View {
        var body: some View {
            Text("Hello, world!")
        }
    }
    

    【讨论】:

    • 这回答了你的问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 2014-06-12
    • 2013-04-30
    • 2021-11-19
    • 1970-01-01
    相关资源
    最近更新 更多