SwiftUI:可以访问祖先自定义的@EnvironmentObject吗?如果是,怎么办?

时间:2019-08-29 20:29:03

标签: swift xcode swiftui

(使用Xcode 11 beta 7)是否可以访问祖先的自定义EnvironmentObject?如果可以,怎么办?并使用无限深度?

比方说,我们在导航中有很多深度层次,例如

UIScene-> MainView-> TabView(其中包含一个标签:)-> SettingsView-> AboutView-> AppVersionView

开始将MainView视为深度级别1,而不考虑TabView,我们的AppVersionView在导航堆栈中处于深度级别4。

假设我们需要使用一些自定义依赖项,例如RESTClient或AppVersionView中的任何内容。这种依赖是无关紧要的。与此相关的是,我们的SceneDelegate类实例化了这种依赖关系,并且深度为1的视图(MainView)将该视图声明为@EnvirontmentObject以便将其注入。

我当前的解决方案是MainView通过将其向下注入到堆栈中的下一个视图SettingsView来“手动”转发它。然后在SettingsView中,我再次手动转发它。

这是一个简化的示例,实际上我在my SwiftUI app的整个使用过程中都具有多个依赖关系,并且在整个导航堆栈中都有不同的层次。

我的想法是,是否有可能以某种方式读取/访问这些注入的EnvironmentObject?在最佳情况下,从任何较早的祖先递归地进行。

如果我正确理解了SwiftUI,那么ViewModifiers就是这种情况。如果将ViewModifier .foregroundColor(.red)添加到MainView,则应将其作为系统范围的默认颜色通过整个应用程序传递(继承)。

我希望使用自定义的EnvironmentObject可以完成同样的事情。而且我知道我们可以像EnvironmentValues

这样的视图访问预定义的(非自定义)this example from Mecid's great blog:
struct ButtonsView: View {
@Environment(\.sizeCategory) var sizeCategory

var body: some View {
    Group {
        if sizeCategory == .accessibilityExtraExtraExtraLarge {
            VStack {
                buttons
            }
        } else {
            HStack {
                buttons
            }
        }
    }
}

}

1 个答案:

答案 0 :(得分:2)

使用environmentObject修饰符放入环境中的对象通过TabViewNavigationView向下传递到所有后代视图的视图层次结构中。您无需做任何特殊的事情(例如手动转发)就可以做到这一点。

在此示例中,在SceneDelegate中,我将使用MainView修饰符将模型对象存储在environmentObject的环境中。模型对象具有appVersion字符串属性。我将使用@EnvironmentObject中的AppVersionView属性访问模型对象,并使用模型的appVersion来填充Text子视图。

enter image description here

代码如下:

import UIKit
import SwiftUI

class Model: ObservableObject {
    @Published var appVersion: String = "version-1"
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = scene as? UIWindowScene else { return }

        let model = Model()
        let contentView = MainView().environmentObject(model)
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}

struct MainView: View {
    var body: some View {
        TabView {
            NavigationView {
                SettingsView()
            }
            .tabItem {
                Image(systemName: "gear")
                Text("Settings") }
        }
    }
}

struct SettingsView: View {
    var body: some View {
        List {
            NavigationLink("About", destination: AboutView())
        }
    }
}

struct AboutView: View {
    var body: some View {
        List {
            NavigationLink("App Version", destination: AppVersionView())
        }
    }
}

struct AppVersionView: View {
    @EnvironmentObject var model: Model

    var body: some View {
        VStack {
            Text("App Version View")
            Text(model.appVersion)
        }
        .padding()
        .border(Color.black)
    }
}