【问题标题】:How to create a singleton that I can update in SwiftUI如何创建一个我可以在 SwiftUI 中更新的单例
【发布时间】:2021-05-08 18:58:08
【问题描述】:

我想创建一个用于显示 loadingView 的全局变量,我尝试了很多不同的方法,但不知道怎么做。当我更改单例的布尔值时,我需要能够在整个应用程序中访问此变量并更新 MotherView 文件。

struct MotherView: View {
    
    @StateObject var viewRouter = ViewRouter()
        
    var body: some View {
        
        if isLoading { //isLoading needs to be on a singleton instance
            Loading()
        }

        
        switch viewRouter.currentPage {
        case .page1:
            ContentView()
        case .page2:
            PostList()
        }
    }
}

struct MotherView_Previews: PreviewProvider {
    static var previews: some View {
        MotherView(viewRouter: ViewRouter())
    }
}

我尝试了下面的单例,但它不允许我更新共享实例?如何更新单例实例?

    struct LoadingSingleton {
    static let shared = LoadingSingleton()
    var isLoading = false

    private init() { }
}

【问题讨论】:

    标签: ios swift xcode swiftui


    【解决方案1】:

    使用@Published 属性使您的单身成为ObservableObject

    struct ContentView: View {
        @StateObject var loading = LoadingSingleton.shared
        
        var body: some View {
            if loading.isLoading {
                Text("Loading...")
            }
            ChildView()
            Button(action: { loading.isLoading.toggle() }) {
                Text("Toggle loading")
            }
        }
    }
    
    struct ChildView : View {
        @StateObject var loading = LoadingSingleton.shared
    
        var body: some View {
            if loading.isLoading {
                Text("Child is loading")
            }
        }
    }
    
    class LoadingSingleton : ObservableObject {
        static let shared = LoadingSingleton()
        @Published var isLoading = false
        
        private init() { }
    }
    

    我应该提一下,在 SwiftUI 中,通常使用 .environmentObject 通过视图层次结构传递依赖项,而不是使用单例——这可能值得研究。

    【讨论】:

    • 有没有绕过需要使用@StateObject 和@ObservedObject?那是我使用单例路线的目标。 @jnpdx
    • 不是真的...我的意思是,您可以尝试使用 Publishers 制作一个 Combine 版本,但这似乎是一场艰苦的战斗,您最终将不得不添加大量样板文件每个视图的代码。 @StateObject@ObservedObject 是属性包装器,有助于告诉 SwiftUI 在发生变化时更新视图。否则,View 不知道要更新。您是否有理由避免使用它们?
    • @jnpdx 如何使用。上述上下文中的环境对象?我在一些 ViewModel 中更新 isLoading 变量并尝试在另一个 swiftUI 文件中使用它。但值不会更新
    • @Honey 很难在没有看到minimal reproducible example 的情况下说出问题所在
    • @jnpdx 这是我的问题stackoverflow.com/questions/69628795/…。请建议
    【解决方案2】:

    首先,将LoadingSingleton 设为遵守ObservableObject 协议的类。在 isLoading 上使用 @Published 属性包装器,以便您的 SwiftUI 视图在更改时更新。

    class LoadingSingleton: ObservableObject {
        @Published var isLoading = false
    }
    

    然后,将LoadingSingleton 放入您的SceneDelegate 并通过environmentObject() 将其挂接到您的SwiftUI 视图中:

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
        static let singleton = LoadingSingleton()
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            let contentView = ContentView()
    
            if let windowScene = scene as? UIWindowScene {
                let window = UIWindow(windowScene: windowScene)
                window.rootViewController = UIHostingController(rootView: contentView.environmentObject(SceneDelegate.singleton))
                self.window = window
                window.makeKeyAndVisible()
            }
        }
    }
    

    要让您的 SwiftUI 视图在更改 isLoading 时更新,请在视图的结构中声明一个变量,如下所示:

    struct MyView: View {
        @EnvironmentObject var singleton: LoadingSingleton
    
        var body: some View {
            //Do something with singleton.isLoading
        }
    }
    

    当您想更改 isLoading 的值时,只需通过 SceneDelegate.singleton.isLoading 访问它,或者在 SwiftUI 视图中通过 singleton.isLoading 访问它。

    【讨论】:

      猜你喜欢
      • 2023-02-16
      • 2012-01-15
      • 1970-01-01
      • 2019-05-05
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      相关资源
      最近更新 更多