无法在视图之间共享环境

Posted

技术标签:

【中文标题】无法在视图之间共享环境【英文标题】:Unable to share environment between Views 【发布时间】:2019-07-27 22:49:10 【问题描述】:

环境: Xcode 11.0 beta 4 (11M374r)

我无法与第二个视图共享“环境”。

我已经在 SceneDelegate 中实例化了环境 BindableObject:

SceneDelegate.swift:

我在基础视图 (ContentView) 和详细视图中都使用了 @EnvironmentObject。

环境已经在 SceneDelegate 中设置好了,所以它应该对所有视图都可用。

ContentView 确实看到了环境。 但是 DetailView 爆炸了:

这是完整的代码:

import Combine
import SwiftUI

struct UserInfo 
    var name: String
    var message: String
    init(name: String, msg: String) 
        self.name = name; self.message = msg
    


// A BindableObject is always a class; NOT a struct.
class UserSettings: BindableObject 
    let willChange = PassthroughSubject<Void, Never>()
    var userInfo = UserInfo(name: "Ric", msg: "Mother had a feeling, I might be too appealing.") 
        didSet 
            willChange.send()
        
    


// =====================================================================================================

struct DetailView: View 
    @Binding var dismissFlag: Bool
    @EnvironmentObject var settings: UserSettings  // ...<error source>

    var body: some View 
        VStack 
            Spacer()
            Button(action: dismiss) 
                Text("Dismiss")
                    .foregroundColor(.white)
            
            .padding()
            .background(Color.green)
            .cornerRadius(10)
            .shadow(radius: 10)

            Text("Hello")
            Spacer()
        
    

    private func dismiss() 
        settings.userInfo.message = "Rubber baby buggy bumpers."
        dismissFlag = false
    


// ---------------------------------------------------------------------------
// Base View:

struct ContentView: View 
    @State var shown = false
    @EnvironmentObject var settings: UserSettings

    var body: some View 
        VStack 
            Spacer()
            Button(action: 
                self.settings.userInfo.name = "Troglodyte"
                self.settings.userInfo.message = "Top Secret"
                self.shown.toggle()
            ) 
                Text("Present")
                    .foregroundColor(.white)
            .sheet(isPresented: $shown)  DetailView(dismissFlag: self.$shown) 
                .padding()
                .background(Color.red)
                .cornerRadius(10)
                .shadow(radius: 10)
            Text(self.settings.userInfo.message)
            Spacer()
        
    


// =====================================================================================================

#if DEBUG
struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

#endif

我错过了什么? 我做错了什么?


根据建议修订:
import SwiftUI
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate 
    var window: UIWindow?
    var userSettings = UserSettings()

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 

        // Use a UIHostingController as window root view controller
        if let windowScene = scene as? UIWindowScene 
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(userSettings))
            self.window = window
            window.makeKeyAndVisible()
        
    

他修改 SceneDelegate 后的运行时错误消息:


这里有一个线索:

【问题讨论】:

查看我编辑的答案以解决您的第二个问题。现在我知道为什么我没有花太多时间在预览上。 :-) 【参考方案1】:

SceneDelegate 中你需要声明你的变量的一个实例:

var userSettings = UserSettings()  // <<--- ADD THIS


func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
    if let windowScene = scene as? UIWindowScene 
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: ContentView()
            .environmentObject(userSettings)  <<-- CHANGE THIS
        )
        self.window = window
        window.makeKeyAndVisible()
    

这会创建一个userSettings 的全局/环境实例。

编辑:

发生了第二个错误,与将您的 @EnvironmentObject 暴露给预览有关。根据this answer by @MScottWaller,您需要在SceneDelegate PreviewProvider 中创建一个单独的实例。

#if DEBUG
struct ContentView_Previews : PreviewProvider 
    static var previews: some View 
        ContentView().environmentObject(UserSettings())


#endif

【讨论】:

根据您的建议进行更改。但我仍然收到运行时错误?(请参阅上面的修订版)。 这是一个不同的运行时错误。我不使用预览,所以请给我几分钟,我会编辑我的答案。

以上是关于无法在视图之间共享环境的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 应用程序和今日视图扩展程序之间共享数据

在视图之间的公共标题视图中设置共享标题;每个 Active View

在 SwiftUI MVVM 中的子视图和父视图之间共享值

SAIKU UI 在视图之间共享全局变量

你如何在 UIKit 视图控制器和它呈现的 SwiftUI 视图之间共享数据模型?

如何使用 AppDelegate 在视图之间共享 iAd 横幅