SwiftUI - Dynamic List filtering animation flies to right side when data source is empty

会有一股神秘感。 提交于 2021-01-27 06:19:20

问题


I have a List that gets data from my people array and displays their names.

I'm also filtering the list so that it only shows the names that contain the text field's text, which is searchText. Here's my code:

struct Person: Identifiable {
    let id = UUID() /// required for the List
    
    var name = ""
}

struct ContentView: View {
    
    @State var searchText = ""
    
    var people = [ /// the data source
        Person(name: "Alex"),
        Person(name: "Ally"),
        Person(name: "Allie"),
        Person(name: "Bob"),
        Person(name: "Tim"),
        Person(name: "Timothy")
    ]
    
    var body: some View {
        
        VStack {
            TextField("Search here", text: $searchText) /// text field
            .padding()
            
            List {
                ForEach(
                    people.filter { person in /// filter the people
                        searchText.isEmpty || person.name.localizedStandardContains(searchText)
                    }
                ) { person in
                    Text(person.name)
                }
            }
            .animation(.default) /// add the animation
        }
    }
}

Without .animation(.default), it doesn't animate the changes (as expected).

With .animation(.default), it animates!

However, the problem happens when none of the people's names contain searchText. When this happens, the people.filter returns an empty array, and the List freaks out. For example, when I type "q", this happens:

The entire list flies to the right, and zooms back in from the left when I delete "q". How can I prevent this from happening? I'm looking for a similar animation to the normal filtering animation (sliding up and disappearing, like in the second gif), or just fading it out.

Edit: iOS 13

I just tested on iOS 13 and if I remove .animation(.default), it works perfectly!

Desired animation on iOS 13

However, if I add .animation(.default) again, I get the same result as in iOS 14.

Edit: List with sections + @mahan's answer

My actual code groups the people, so I already use sections in my List.

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 = ""

    /// groups of people
    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 people that match searchText
                    groups.filter { group in
                        searchText.isEmpty || group.people.contains(where: { person in
                            person.name.localizedStandardContains(searchText)
                        })
                    }
                ) { group in
                    Section(header: Text(group.groupName)) {
                        ForEach(

                            /// filter the people in each group
                            group.people.filter { person in
                                searchText.isEmpty || person.name.localizedStandardContains(searchText)
                            }
                        ) { person in
                            Text(person.name)
                        }
                    }
                }
                
            }
            .animation(.default) /// add the animation
        }
    }
}

If I wrap the ForEach inside the List as @mahan suggested, this happens:

The List animates perfectly with no weird zoom animation, but the section headers lose their styles and look like normal rows. But I think we're getting close!


回答1:


Wrap ForEach in a Section, and the issue will be fixed.

        List {
            Section {
                ForEach(
                    people.filter { person in /// filter the people
                        searchText.isEmpty || person.name.localizedStandardContains(searchText)
                    }
                ) { person in
                    Text(person.name)
                }
            }
        }
        .animation(.default) /// add the animation

Update

You can add as many sections as you wish so.

struct Person: Identifiable {
    let id = UUID() /// required for the List
    
    var name = ""
}

struct ContentView: View {
    
    @State var searchText = ""
    
    var people = [ /// the data source
        Person(name: "Alex"),
        Person(name: "Ally"),
        Person(name: "Allie"),
        Person(name: "Bob"),
        Person(name: "Tim"),
        Person(name: "Timothy")
    ]
    
    var people2 = [ /// the data source
        Person(name: "John"),
        Person(name: "George"),
        Person(name: "Jack"),
        Person(name: "Mike"),
        Person(name: "Barak"),
        Person(name: "Steve")
    ]
    
    var body: some View {
        
        VStack {
            TextField("Search here", text: $searchText) /// text field
                .padding()
            
            List {
                Section {
                    ForEach(
                        people.filter { person in /// filter the people
                            searchText.isEmpty || person.name.localizedStandardContains(searchText)
                        }
                    ) { person in
                        Text(person.name)
                    }
                }
                
                Section {
                    ForEach(people2.filter { person in /// filter the people
                        searchText.isEmpty || person.name.localizedStandardContains(searchText)
                    }) { person in
                        Text(person.name)
                    }
                }
            }
            .animation(.default) /// add the animation
        }
    }
}


来源:https://stackoverflow.com/questions/65065406/swiftui-dynamic-list-filtering-animation-flies-to-right-side-when-data-source

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!