首页 > 解决方案 > SwiftUI - 数据源为空时动态列表过滤动画飞到右侧

问题描述

我有一个从我的数组List中获取数据并显示它们的名称。people

我还在过滤列表,以便它只显示包含文本字段文本的名称,即searchText. 这是我的代码:

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
        }
    }
}

没有.animation(.default),它不会为更改设置动画(如预期的那样)。

Filtering without animation

随着 .animation(.default),它动画!

Filtering with animation

但是,当人名中没有一个包含searchText. 发生这种情况时,people.filter返回一个空数组,然后List吓坏了。例如,当我输入“q”时,会发生这种情况:

List zooms to the right

整个列表飞到右边,当我删除“q”时从左边放大。我怎样才能防止这种情况发生?我正在寻找与普通过滤动画类似的动画(向上滑动和消失,就像在第二个 gif 中一样),或者只是将其淡出。

编辑:iOS 13

我刚刚在 iOS 13 上进行了测试,如果我删除.animation(.default)它,它可以完美运行!

iOS 13 上所需的动画

但是,如果我.animation(.default)再次添加,我会得到与 iOS 14 相同的结果。

与 iOS 14 相同的结果

编辑:列出部分 + @mahan的答案

我的实际代码将人们分组,所以我已经sections我的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
        }
    }
}

sorting List with sections

如果我按照@mahanForEach的建议包装内部,就会发生这种情况:List

动画完美,没有奇怪的List缩放动画,但部分标题失去了样式,看起来像正常的行。但我认为我们正在接近!

标签: iosswiftswiftuiswiftui-list

解决方案


将ForEach包装在Section中,问题将得到解决。

        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

更新

您可以根据需要添加任意数量的部分。

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
        }
    }
}

推荐阅读