在SwiftUI分组表中按下“编辑模式”按钮之前,行不会被删除

时间:2019-08-23 13:45:58

标签: swiftui swiftui-list

我在SwiftUI中使用ObservableObject作为数据源实现了分组表。嵌套的ForEach用于生成每个部分。 EditMode()按钮可切换该Environment属性。在编辑模式下,完成删除操作后,被删除的行(意外地)仍保留在屏幕上。 (即使已从数据源阵列中删除了该对象。)当用户返回正常查看模式时,该对象也会从表中延迟删除。

enter image description here

为了尝试查找错误:

  • 数据源对象符合Hashable,Identifiable和Equatable。

  • 实现了一个简单的删除操作(即 删除@Published属性中的第一个对象)

  • 数据源/视图模型存储在@EnvironmentData对象中

因此,简单的问题是我做了什么错,这会导致SwiftUI不能立即在EditMode(对我来说是按科目分组)列表上反映出EditMode中的删除操作?

struct ContentView: View {

    @EnvironmentObject var vm: AppData

    var body: some View {

        NavigationView {

            List {
                ForEach(vm.folderSource) { (folder: Folder)   in
                    return Section(header: Text(folder.title)) {
                        //this is where problem originates. When I drop in a new full-fledged View struct, UI updates stop working properly when .onDelete is called from this nested View
                        FolderView(folder: folder)
                    }
                }
            }.listStyle(GroupedListStyle())
                .navigationBarItems(trailing: EditButton())
        }
    }
}

struct FolderView: View {

    var folder: Folder

    @EnvironmentObject var vm: AppData


    var body: some View {
        //I'm using a dedicated View inside an outer ForEach loop to be able to access a data-source for each dynamic view.

        let associatedProjects = vm.projects.filter{$0.folder == folder}

        return ForEach(associatedProjects) { (project: Project) in
            Text(project.title.uppercased())
            // dumbed-down delete, to eliminate other possible issues preventing accurate Dynamic View updates
        }.onDelete{index in self.vm.delete()}
    }
}


//view model
class AppData: ObservableObject {



    let folderSource: [Folder]
    @Published var projects: [Project]

    func delete() {
        //dumbed-down static delete call to try to find ui bug
        self.projects.remove(at: 0)
        //
    }


    init() {
        let folders = [Folder(title: "folder1", displayOrder: 0), Folder(title: "folder2", displayOrder: 1), Folder(title: "folder3", displayOrder: 2)  ]

        self.folderSource = folders


        self.projects = {

            var tempArray = [Project]()
            tempArray.append(Project(title: "project 0", displayOrder: 0, folder: folders[0]  ))
            tempArray.append(Project(title: "project 1", displayOrder: 1, folder: folders[0]  ))
            tempArray.append(Project(title: "project 2", displayOrder: 2, folder: folders[0]  ))


            tempArray.append(Project(title: "project 3", displayOrder: 0, folder: folders[1]  ))
            tempArray.append(Project(title: "project 4", displayOrder: 1, folder: folders[1]  ))
            tempArray.append(Project(title: "project 5", displayOrder: 2, folder: folders[1]  ))


            tempArray.append(Project(title: "project 6", displayOrder: 0, folder: folders[2]  ))
            tempArray.append(Project(title: "project 7", displayOrder: 1, folder: folders[2]  ))
            tempArray.append(Project(title: "project 8", displayOrder: 2, folder: folders[2]  ))

            return tempArray
        }()

    }

}


//child entity many-to-one (Folder)
class Project: Hashable, Equatable, Identifiable {

    let id = UUID()
    let title: String
    let displayOrder: Int
    let folder: Folder

    init(title: String, displayOrder: Int, folder: Folder) {
        self.title = title
        self.displayOrder = displayOrder
        self.folder = folder
    }

    static func == (lhs: Project, rhs: Project) -> Bool {
        lhs.id == rhs.id

    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

//parent entity: Many Projects have one Folder
class Folder: Hashable, Equatable, Identifiable {

    let id = UUID()
    let title: String
    let displayOrder: Int


    init(title: String, displayOrder: Int) {
        self.title = title
        self.displayOrder = displayOrder
    }

    //make Equatable
    static func == (lhs: Folder, rhs: Folder) -> Bool {
        lhs.id == rhs.id
    }

    //make Hashable
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

在SceneDelegate.swift中

 // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView().environmentObject(AppData())

2 个答案:

答案 0 :(得分:1)

我删除了我以前的答案,因为正如您所指出的那样,尽管它的确有效,但这纯粹是巧合。

您在这里还有其他工作。它基本上是通过封装第二个 ForEach 来工作的。到目前为止,我发现封装是避免某些错误的好工具。在这种情况下,情况恰恰相反!

struct ContentView: View {

    @EnvironmentObject var vm: AppData

    var body: some View {

        NavigationView {

            List {
                ForEach(vm.folderSource) { (folder: Folder)   in
                    Section(header: Text(folder.title)) {
//                        FolderView(folder: folder)
                        ForEach(self.vm.projects.filter{$0.folder == folder}) { (project: Project) in
                            Text(project.title.uppercased())
                        }.onDelete{index in
                            self.vm.delete()
                        }
                    }
                }
            }
            .listStyle(GroupedListStyle())
            .navigationBarItems(trailing: EditButton())
        }
    }
}

答案 1 :(得分:0)

所以,奇怪的是,@ kontiki的(有用的)解决方案只是出于巧合。事实证明,只需在FolderView中添加一个(未使用的)函数类型变量作为View属性参数,然后使用该函数参数在init方法中设置State / Environment类型的包装变量就可以解决此问题。这是莫名其妙的。

WORKS(设置包装状态属性的附加函数参数['vm'是AppData视图模型的变量名称,符合ObservableObject]。请参见上文。)

FolderView(folder: folder, onDelete: {self.vm.hello = "ui update bug goes away, even though this function not called"}) //function sets EnvironmentObject-type property

不工作(添加未设置包装状态属性的函数参数

FolderView(folder: folder, onDelete: {print("ui update bug still here")})

不起作用(添加非功能参数)

FolderView(folder: folder, unusedString: "ui update bug still here") 

我提交了一个错误报告,因为(在我看来)这是意外行为。