SwiftUI - 在动态过滤列表时,防止从 flying/zooming 到右侧的部分

SwiftUI - Prevent Sections from flying/zooming to the right in List when dynamically filtering them

我最初问这个问题:

在那里,我有一个 List 没有部分。我正在过滤它们,以便它只显示包含 TextField 内文本的行。解决方案是将 List 内的所有内容包装在 Section.

不幸的是,我现在需要过滤 Sections。这是我的代码:

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

结果:

我在 ForEach 中传入过滤数组以确定 Section。但是,每当该数组发生变化时, List 的动画就会非常奇怪。 Sections zoom/fly 到右侧,当数组再次包含它们时从左侧返回。 我怎样才能避免这个动画?

如果我删除 .animation(.default),它根本不会像预期的那样具有动画效果。但是,我还是想要一个动画。有没有办法淡化更改或滑动更改?

解决方案是不使用列表。只要你不使用选择和行删除 ScrollView 基本上是相同的。

如果你想让它的样式有点像 List 那也不难:

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

看起来与默认列表几乎相同: