[英]SwiftUI optional environment object
我像這樣使用@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
的參數。 ei 這些不起作用:
@EnvironmentObject var object: MyObject = MyObject()
@EnvironmentObject(MyObject()) var object: MyObject
我試圖將@EnvironmentObject
包裝在我自己的屬性包裝器中,但這根本不起作用。
我也試過包裝對 object 屬性的訪問,但它不會拋出一個可以被捕獲的異常,它會拋出一個fatalError
。
有什么我遺漏的嗎,或者我只是在嘗試不可能的事情?
如果 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")
}
}
通過符合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
}
享受!
我知道您告訴過您無法將對象放入包裝器中,但是我認為此解決方案是實現您想要的目標的好方法。
您唯一要做的就是創建一個非可選的包裝器,但它將包含您的可選對象:
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)
}
}
我制作了一個基於StateObject
的包裝器,並通過@autoscaping
閉包延遲初始化默認值。
@EnvironmentModel var object = Object() //default value
此外,如果您通過環境傳遞對象,則不必將其存儲在某處
yourView.environmentModel(Object())
代碼
@propertyWrapper
public struct EnvironmentModel<Model: ObservableObject>: DynamicProperty {
@StateObject private var object = Object()
@Environment(\.environmentModel) private var environment
private let defaultValue: () -> Model
private let id: AnyHashable
public var wrappedValue: Model {
createModel()
return object.model
}
public var projectedValue: Binding<Model> {
createModel()
return $object.model
}
public init(wrappedValue: @escaping @autoclosure () -> Model) {
defaultValue = wrappedValue
id = String(reflecting: Model.self)
}
public init<ID: Hashable>(wrappedValue: @escaping @autoclosure () -> Model, _ id: ID) {
defaultValue = wrappedValue
self.id = id
}
@inline(__always) private func createModel() {
guard object.model == nil else { return }
object.model = (environment[id] as? () -> Model)?() ?? defaultValue()
}
private final class Object: ObservableObject {
var model: Model! {
didSet {
model.objectWillChange.subscribe(objectWillChange).store(in: &bag)
}
}
var bag: Set<AnyCancellable> = []
let objectWillChange = PassthroughSubject<Model.ObjectWillChangePublisher.Output, Model.ObjectWillChangePublisher.Failure>()
init() {}
}
}
extension View {
public func environmentModel<M: ObservableObject>(_ model: @escaping @autoclosure () -> M) -> some View {
modifier(EnvironmentModelModifier(model: model, key: String(reflecting: M.self)))
}
public func environmentModel<M: ObservableObject, ID: Hashable>(id: ID, _ model: @escaping @autoclosure () -> M) -> some View {
modifier(EnvironmentModelModifier(model: model, key: id))
}
}
private struct EnvironmentModelModifier<Model>: ViewModifier {
@State private var object = Object()
private let create: () -> Model
let key: AnyHashable
var model: Model {
createModel()
return object.model
}
init(model: @escaping () -> Model, key: AnyHashable) {
create = model
self.key = key
}
@inline(__always) private func createModel() {
guard object.model == nil else { return }
object.model = create()
}
func body(content: Content) -> some View {
let value: () -> Model = { self.model }
return content.environment(\.environmentModel[key], value)
}
private final class Object {
var model: Model!
init() {}
}
}
private enum EnvironmentModelKey: EnvironmentKey {
static var defaultValue: [AnyHashable: Any] { [:] }
}
extension EnvironmentValues {
fileprivate var environmentModel: [AnyHashable: Any] {
get { self[EnvironmentModelKey.self] }
set { self[EnvironmentModelKey.self] = newValue }
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.