在视图中重新实例化 @EnvironmentObject

Reinstantiate an @EnvironmentObject in a view

在我的应用程序中,我需要在点击按钮时重新实例化 @EnvironmentObject。哪里才是正确的做法?

这是我的视图的屏幕截图 - 在存档对象列表中,我想要不可变对象。所以,当我点击 Archive Object 时,我想将当前对象添加到数组并创建一个新的当前对象。

该应用程序的简化版本如下所示。

我实例化对象在SceneDelegate:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    var myObject = MyObject()
    let myObjects = MyObjects()

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        let contentView = ContentView()
            .environmentObject(myObject)
            .environmentObject(myObjects)

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

    //.....
}

这是我的模型:

class MyObject: ObservableObject, Identifiable {
    @Published var id = UUID()
}

class MyObjects: ObservableObject {
    @Published var values: [MyObject] = [
        MyObject(),
        MyObject()
    ]
}

在我看来,我需要将我的当前对象放入一个(存档)数组,然后创建一个新的当前对象来使用。这是我正在尝试的:

struct ContentView: View {

    @EnvironmentObject var currentObject: MyObject
    @EnvironmentObject var objects: MyObjects

    var sceneDelegate: UISceneDelegate {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
            let sd = windowScene.delegate as? SceneDelegate else { fatalError() }
        return sd
    }

    var body: some View {
        VStack {
            Form {
                Section(header: Text("Current Object")) {
                    Text(currentObject.id.uuidString)
                }
                Section(header: Text("Archived Objects")) {
                    List(objects.values, id: \.id) { object in
                        Text(object.id.uuidString)

                    }
                    Button(action: {
                        self.objects.values.append(self.currentObject)

// as the environment object is get only, I cannot reinstantiate it here...
//                        self.currentObject = MyObject()
                    }) {
                        Text("Archive Object")
                    }
                }
            }

            Spacer()
        }
    }
}

这是一个解决方案。查看内联评论。

测试 Xcode 11.4 / iOS 13.4

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {

        // Usage
        ContentView(current: MyObject())
            .environmentObject(MyObjects())
    }
}


struct ContentView: View {
    @EnvironmentObject var objects: MyObjects
    @State private var current: MyObject        // tracks current

    init(current: MyObject) {
        _current = State(initialValue: current)
    }

    var body: some View {
        // rebuilt on *current* changed
        ContentInnerView(current: $current)
            .environmentObject(current)
    }

    struct ContentInnerView: View {
        @Binding var current: MyObject   // change current

        @EnvironmentObject var objects: MyObjects
        @EnvironmentObject var currentObject: MyObject

        init(current: Binding<MyObject>) {
            _current = current
        }

        var sceneDelegate: UISceneDelegate {
            guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
                let sd = windowScene.delegate as? SceneDelegate else { fatalError() }
            return sd
        }

        var body: some View {
            VStack {
                Form {
                    Section(header: Text("Current Object")) {
                        Text(currentObject.id.uuidString)
                    }
                    Section(header: Text("Archived Objects")) {
                        List(objects.values, id: \.id) { object in
                            Text(object.id.uuidString)

                        }
                        Button(action: {
                            self.objects.values.append(self.currentObject)
                            self.current = MyObject() // << works !!
                        }) {
                            Text("Archive Object")
                        }
                    }
                }

                Spacer()
            }
        }
    }
}