SwiftUI:可以访问祖先自定义`@EnvironmentObject`?如果是,如何?
Posted
技术标签:
【中文标题】SwiftUI:可以访问祖先自定义`@EnvironmentObject`?如果是,如何?【英文标题】:SwiftUI : possible to access ancestor custom `@EnvironmentObject`? If yes, how? 【发布时间】:2019-08-29 20:29:03 【问题描述】:(使用 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 中有多个依赖项,正在使用和整个导航堆栈中的不同层。
我的想法是是否有可能以某种方式读取/访问这些注入的 EnvironmentObjects?在最好的情况下,从任何早期的祖先递归。
如果我正确理解了 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】:使用environmentObject
修饰符放入环境的对象通过TabView
和NavigationView
向下传递到所有后代视图。您无需执行任何特殊操作(例如手动转发)即可实现此目的。
在此示例中,在SceneDelegate
中,我将使用environmentObject
修饰符将模型对象存储在MainView
的环境中。模型对象有一个appVersion
字符串属性。我将使用AppVersionView
中的@EnvironmentObject
属性访问模型对象,并使用模型的appVersion
填充Text
子视图。
代码如下:
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)
【讨论】:
是的,但是我使用 MVVM 并且每个视图都有一个被注入的 ViewModel。我的任何 ViewModel 都有 1-5 个依赖项。我希望 skippinh havinh 转发这些依赖项。起初我认为它与问题无关,但现在我明白它确实是,因为函数func envirormentObject
是 View
协议的函数。
并且将所有这些依赖项注入到视图中,这与 MVVM 的想法几乎背道而驰......
并且其中一些依赖项要到稍后才能创建,因此如果它们不是可选的,它们不能全部放入某个上帝 Depencendies
对象并被传递,这很难看...
但是谢谢你!答案是“它可以工作!”,这也是不使用 MVVM 的原因。
@Saijon,定义“注入”。你的意思是内存占用?如果做得好,一个环境对象只有一个实例。 (不是 byVal,是 byRef。)以上是关于SwiftUI:可以访问祖先自定义`@EnvironmentObject`?如果是,如何?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中将自定义字体系列设置为整个应用程序的默认字体
是否可以创建导航祖先的自定义 jQuery 选择器?例如:closest 或 :parents 选择器