Строка не удаляется до тех пор, пока не будет нажата кнопка "Изменить режим" в сгруппированной таблице SwiftUI
Я реализовал сгруппированную таблицу в SwiftUI, используя ObservableObject в качестве источника данных. Затем я реализовал вложенный ForEach для генерации каждого раздела и кнопку EditMode(). В режиме редактирования, когда действие удаления завершено, удаленная строка остается на экране. Даже если объект был удален из массива источника данных. Когда пользователь возвращается в обычный режим просмотра, объект удаляется из таблицы с анимацией.
Чтобы попытаться отследить ошибку:
Я сделал объекты источника данных совместимыми с Hashable, Identifiable и Equatable, чтобы исключить возможность ошибочной идентификации в циклах ForEach.
Я реализовал самое простое из возможных действий по удалению (которое заключается в простом удалении свойства первого объекта @Published array)
Я сохранил источник данных / модель представления в @EnvironmentData, чтобы гарантировать, что иерархия представления положительно имела ссылку на тот же источник данных
Я проверил бета-версию Xcode на предмет упоминания проблемы onDelete
Поэтому простой вопрос заключается в том, что я сделал неправильно, из-за чего SwiftUI не сразу отображает действие удаления в EditMode в очень простом (я думаю) сгруппированном (по разделу) списке?
import SwiftUI
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)
}
}
}
2 ответа
Я удалил свой предыдущий ответ, поскольку, как вы отметили, хотя это сработало, это было просто совпадение.
Здесь у вас есть еще одна работа вокруг. Это в основном работает, не инкапсулируя второй 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())
}
}
}
Таким образом, в самом странном повороте решение @kontiki (полезное) сработало по чистой случайности. Оказывается, что простое добавление (неиспользуемой) переменной типа функции в FolderView в качестве параметра свойства View и использование этого параметра функции для установки обернутой переменной типа State/Environment в методе init решает проблему. Что необъяснимо. Абсолютно бессмысленно.
РАБОТАЕТ (добавить параметр функции, который устанавливает свойство обернутого состояния ['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")
Я подал отчет об ошибке, так как (на мой взгляд) это все неожиданное поведение, а не верное решение, основанное на любом задокументированном или желаемом пути кодирования