调用 objectWillChange.send() 会产生性能问题

Calling objectWillChange.send() produce performance issues

提问人:Alex Filatov 提问时间:11/6/2023 最后编辑:Alex Filatov 更新时间:11/6/2023 访问量:70

问:

我有一个具有嵌套对象结构的项目

Project -> Panels -> Layers

我有 SettingsView,我在其中更新任何这些结构的不同属性。

每次更新时,我都必须打电话才能立即查看 View 中的更改。 如果我有少量对象,它可以正常工作并且不会产生性能问题。但是,如果对象数量增加,则会出现性能问题。objectWillChange.send()

我的代码示例如下:

class SelectedProjectHolder: ObservableObject {
    @Published var projects: [Project] = []
    @Published var selectedProject: Project? = nil
.....
    func updateTextLayerColor(for textLayer: TextLayer, newColor: Color) {
        guard let selectedProject = selectedProject else {
            return
        }
        
        for (panelIndex, panel) in selectedProject.panels.enumerated() {
            if let layerIndex = panel.textLayers.firstIndex(where: { $0.id == textLayer.id }) {
                selectedProject.panels[panelIndex].textLayers[layerIndex].color = newColor
                objectWillChange.send()
                return
            }
        }
    }
....
}

struct MainApp: App {
    
    let selectedProjectHolder = SelectedProjectHolder()
var body: some Scene {
        WindowGroup {
            ContentView(lastSaveTime: lastSaveTime)
                .environmentObject(selectedProjectHolder)
        }
        .commands {
            SidebarCommands()
        }
    }
}

// example of Panel view with passed data:
struct PanelView: View {
    @EnvironmentObject var selectedProjectHolder: SelectedProjectHolder
    @Binding var panel: Panel
...
}

我错过了什么,为什么它不显式调用就不起作用?objectWillChange.send()

有没有办法在我更改属性时立即查看更改而无需调用?objectWillChange.send()selectedProjectSelectedProjectHolder

iOS Swift macOS SwiftUI Core-Data

评论

1赞 lorem ipsum 11/6/2023
项目应该是 struct,您还需要一个 StateObject
0赞 jnpdx 11/6/2023
这纯粹是推测性的,因为您没有包括一些最重要的部分来诊断这一点(定义以及您如何观察您期望更新的内容),但我猜其中一个或两个:1) 是 2) 您实际上并没有在您的ProjectSelectedProjectHelperViewProjectclass@EnvironmentObjectView
0赞 Alex Filatov 11/6/2023
是的,Project 是一个类,尽管现在我正在尝试将其转换为结构。是否将其作为类不允许视图显示更新的状态?P.S. 如果有帮助,我添加了一些 PanelView 定义@Binding var
0赞 jnpdx 11/6/2023
如果是类,则必须手动调用 send。如果你的数据结构都是结构,它将自动发生。
0赞 lorem ipsum 11/7/2023
stackoverflow.com/questions/68710726/swiftui-view-updating/......

答:

1赞 malhal 11/6/2023 #1

代码结构不正确。该对象应仅保存项目,并应负责加载和保存。所选项目应位于 ProjectsView 中。或者,在公共父项中状态,将@Binding传递给可以更改选择的子视图,并作为 let 传递给仅读取当前选择的子视图。@State

创建一个计算属性,将所选内容转换为颜色,并将其传递给子 View init。当传入不同的颜色时,将调用正文。不需要对象或 objectWillChange,这个基本的更改检测已经内置在 SwiftUI 视图结构中。

示例代码:

struct ProjectsView: View {
    @EnvironmentObject var projectsStore: ProjectsStore
    @State var selectedProject: Project? = nil

    // transform from the selection to the color
    // this is called every time the selection changes
    var textLayerColor: Color {
        guard let selectedProject = selectedProject else {
            return defaultColor
        }
        ...
        // find the color you want
        ...
        return newColor
    }

    var body: View {
    ...
        ForEach(projectsStore.projects) { project in
            // pass computed property into child View
            PanelView(color: textLayerColor) 
        ...

评论

0赞 Alex Filatov 11/6/2023
感谢您的回复!我试着把我的头缠绕在它身上。据我了解,我仍然可以使用@Binding。但我不明白为什么视图不显示更新的视图,除非我调用?P.S. 如果有帮助,我添加了一些 PanelView 定义objectWillChange.send()
0赞 malhal 11/6/2023
正如我所说,代码结构不正确,所以你有某种技巧来使其工作。最好像我的答案一样正确构建它,如果您需要示例代码,请告诉我。
0赞 Alex Filatov 11/6/2023
是的,一个例子会很棒,请分享!
0赞 malhal 11/6/2023
OK 添加了一个示例,说明如何使用 State 进行选择并使用 Computed 属性转换数据