SwiftUI 可选环境对象

dea*_*rne 12 optional swift swiftui

我是这样使用的@EnvironmentObject

struct MyView: View {
  @EnvironmentObject var object: MyObject

  ...
}
Run Code Online (Sandbox Code Playgroud)

但我的代码不需要为object.

只是使这个可选不起作用(甚至不编译 - Property type 'MyObject?' does not match that of the 'wrappedValue' property of its wrapper type 'EnvironmentObject'

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

@EnvironmentObject var object: MyObject = MyObject()

@EnvironmentObject(MyObject()) var object: MyObject
Run Code Online (Sandbox Code Playgroud)

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

我也试过包装对 object 属性的访问,但它不会抛出一个可以被捕获的异常,它会抛出一个fatalError.

有什么我遗漏的吗,或者我只是在尝试不可能的事情?

Mam*_*yya 12

通过符合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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

struct MyView: View {
    @Environment(\.object) var object
}
Run Code Online (Sandbox Code Playgroud)

享受!

  • 这是反应性的吗? (2认同)
  • 这不是反应性的。@NicoDioso (2认同)

ato*_*oil 11

如果 EnvironmentObject 中的任何内容发生变化(和其他警告),它不是很优雅并且很容易中断,但是如果您在 SwiftUI 1 / Xcode 11.3.1 中打印 EnvironmentObject 您会得到:

EnvironmentObject<X>(_store: nil, _seed: 1)

那么怎么样:

extension EnvironmentObject {
    var hasValue: Bool {
        !String(describing: self).contains("_store: nil")
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 哈哈哈,这真是太糟糕了! (4认同)

Ste*_* G. 5

我知道您告诉过您无法将对象放入包装器中,但是我认为这个解决方案是实现您想要的效果的好方法。

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

class MyObjectWrapper: ObservableObject {

  @Published var object: MyObject?

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

如果任何视图发生变化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)
  }

}
Run Code Online (Sandbox Code Playgroud)