Skip to content

Instantly share code, notes, and snippets.

@crayment
Created February 10, 2021 17:20
Show Gist options
  • Save crayment/f045c07a7d2648492d6ae8831cc5b573 to your computer and use it in GitHub Desktop.
Save crayment/f045c07a7d2648492d6ae8831cc5b573 to your computer and use it in GitHub Desktop.

What?

This gist demonstrates a bug in SwiftUI.List animated updates when quickly applying changes. Run the gist and then tap "Move" and "Back" in quick succession. The list will be put into strange states where section titles show in rows and row titles show in section headers.

import SwiftUI
struct ListAnimationsView: View {
struct Item: Identifiable {
var name: String
var id: String { name }
static var currentID = 0
static func new() -> Item {
currentID += 1
return .init(name: "Row \(currentID)")
}
}
struct Section: Identifiable {
var name: String
var items: [Item]
var id: String {
name
}
}
@State var sections: [Section] = [
Section(name: "Section 1", items: [.new(), .new()]),
Section(name: "Section 2", items: [.new(), .new()]),
]
var body: some View {
NavigationView {
VStack {
HStack {
Button(action: {
sections[0].items.append(Item.new())
}, label: {
Text("Add S1")
})
Button(action: {
guard !sections[0].items.isEmpty else { return }
sections[1].items.append(sections[0].items.removeLast())
}, label: {
Text("Move")
})
Button(action: {
guard !sections[1].items.isEmpty else { return }
sections[0].items.append(sections[1].items.removeLast())
}, label: {
Text("Back")
})
}
List {
ForEach(sections, id: \.id) { section in
SwiftUI.Section(header: Text(section.name)) {
ForEach(section.items, id: \.id) { item in
Text(item.name)
}
.onDelete(perform: { indexSet in
delete(sectionID: section.id, indexSet: indexSet)
})
}
.id(section.id)
}
}
.listStyle(GroupedListStyle())
.animation(.default)
}
}
.navigationBarItems(trailing: trailingNavItems)
}
var trailingNavItems: some View {
HStack {
Button(action: {}, label: {
Image(systemName: "note.text")
})
Spacer(minLength: 20)
Button(action: {}, label: {
Image(systemName: "folder.fill")
})
}
}
func delete(sectionID: String, indexSet: IndexSet) {
guard
let sectionIndex = sections.firstIndex(where: { $0.id == sectionID })
else {
return
}
sections[sectionIndex].items.remove(atOffsets: indexSet)
}
}
struct ListAnimationsView_Previews: PreviewProvider {
static var previews: some View {
ListAnimationsView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment