Reputation: 30506
I originally asked this question:
SwiftUI - Dynamic List filtering animation flies to right side when data source is empty
There, I had a List
without sections. I was filtering them so that it only showed the rows that contained the text inside a TextField
. The solution was to wrap everything inside the List
in a Section
.
Unfortunately, I now need to filter Section
s. Here's my code:
struct Group: Identifiable {
let id = UUID() /// required for the List
var groupName = ""
var people = [Person]()
}
struct Person: Identifiable {
let id = UUID() /// required for the List
var name = ""
}
struct ContentView: View {
@State var searchText = ""
var groups = [
Group(groupName: "A People", people: [
Person(name: "Alex"),
Person(name: "Ally"),
Person(name: "Allie")
]),
Group(groupName: "B People", people: [
Person(name: "Bob")
]),
Group(groupName: "T People", people: [
Person(name: "Tim"),
Person(name: "Timothy")
])
]
var body: some View {
VStack {
TextField("Search here", text: $searchText) /// text field
.padding()
List {
ForEach(
/// Filter the groups for those that contain searchText
groups.filter { group in
searchText.isEmpty || group.groupName.localizedStandardContains(searchText)
}
) { group in
Section(header: Text(group.groupName)) {
ForEach(group.people) { person in
Text(person.name)
}
}
}
}
.animation(.default) /// apply the animation
}
}
}
Result:
I pass in a filtered array in the ForEach
to determine the Section
s. However, whenever that array changes, the List
animates really weirdly. The Section
s zoom/fly to the right side, and come back from the left when the array includes them again. How can I avoid this animation?
If I remove .animation(.default)
, it doesn't animate at all, as expected. But, I would still like an animation. Is there a way to fade the changes, or slide them instead?
Upvotes: 7
Views: 608
Reputation: 3697
The solution is not using List. As long as you're not using selection and row deleting a ScrollView is basically the same.
If you want to style it a bit like the List that's also not that hard:
struct SearchAnimationExample: View {
...
var body: some View {
VStack {
TextField("Search here", text: $searchText) /// text field
.padding()
ScrollView {
VStack(spacing: 0) {
ForEach(
groups.filter { group in
searchText.isEmpty || group.groupName.localizedStandardContains(searchText)
}
) { group in
Section(header: header(title: group.groupName)) {
ForEach(group.people) { person in
row(for: person)
Divider()
}
}
}.transition(.opacity) // Set which transition you would like
// Always full width
HStack { Spacer() }
}
}
.animation(.default)
}
}
func header(title: String) -> some View {
HStack {
Text(title).font(.headline)
Spacer()
}
.padding(.horizontal)
.background(Color.gray.opacity(0.4))
}
func row(for person: Person) -> some View {
HStack {
Text(person.name)
Spacer()
}.padding()
}
}
Looks practically the same as the default list:
Upvotes: 2