SwiftUI可选环境对象

时间:2020-01-20 11:23:37

标签: swift swiftui optional

我正在这样使用@EnvironmentObject

struct MyView: View {
  @EnvironmentObject var object: MyObject

  ...
}

但是我的代码不需要object有一个值。

仅将此选项设置为无效(甚至无法编译-Property type 'MyObject?' does not match that of the 'wrappedValue' property of its wrapper type 'EnvironmentObject'

您也不能传入默认对象(也可以解决我的问题)-作为属性的初始值或@EnvironmentObject的参数。 e.i.这些不起作用:

@EnvironmentObject var object: MyObject = MyObject()

@EnvironmentObject(MyObject()) var object: MyObject

我试图将@EnvironmentObject包装在自己的属性包装器中,但这根本不起作用。

我还尝试了包装对object属性的访问,但是它不会引发可捕获的异常,而是会引发fatalError

我有什么想念的,还是我只是在尝试不可能的事情?

2 个答案:

答案 0 :(得分:2)

通过遵循 EnvironmentKey,您基本上可以提供一个默认值,SwiftUI 可以在丢失的情况下安全地回退到该默认值。此外,您还可以利用 EnvironmentValues 通过基于密钥路径的 API 访问对象。

您可以将两者结合使用,如下所示:

public struct ObjectEnvironmentKey: EnvironmentKey {
    // this is the default value that SwiftUI will fallback to if you don't pass the object
    public static var defaultValue: Object = .init()
}

public extension EnvironmentValues {
    // the new key path to access your object (\.object)
    var object: Object {
        get { self[ObjectEnvironmentKey.self] }
        set { self[ObjectEnvironmentKey.self] = newValue }
    }
}

public extension View {
    // this is just an elegant wrapper to set your object into the environment
    func object(_ value: Object) -> some View {
        environment(\.object, value)
    }
}

现在从视图访问您的新对象:

struct MyView: View {
    @Environment(\.object) var object
}

享受吧!

答案 1 :(得分:0)

我知道您告诉过您您无法将对象放入包装器中,但是我认为此解决方案是实现所需目标的好方法。

您唯一要做的就是创建一个包装器,该包装器将是非可选的,但是将包含您的可选对象:

class MyObjectWrapper: ObservableObject {

  @Published var object: MyObject?

}

然后,您创建视图并将包装器分配给环境:

let wrapper = MyObjectWrapper()
// You can try to load your object here, and set it to nil if needed.
let view = MyView().environmentObject(wrapper)

在您看来,您现在可以检查对象的存在:

struct MyView: View {
  
  @EnvironmentObject var objectWrapper: MyObjectWrapper
  
  var body: some View {
    if objectWrapper.object != nil {
      Text("Not nil")
    } else {
      Text("Nil")
    }
  }
  
}

如果任何视图更改objectWrapper.object,将重新加载该视图。

您可以轻松模拟视图,甚至在几秒钟后触发更改以检查过渡:

struct MyView_Previews: PreviewProvider {

  static var previews: some View {
    let objectWrapper = MyObjectWrapper()
    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
      objectWrapper.object = MyObject()
    }
    return MyView().environmentObject(objectWrapper)
  }

}