How to animate dynamic List sorting with SwiftUI and CoreData (@FetchRequest)
Asked Answered
H

1

8

I have a list that displays a CoreData FetchRequest, and I have a Picker that changes how the list is sorted. The current way I implemented this looks like:

struct ParentView: View {
    enum SortMethod: String, CaseIterable, Identifiable {
        var id: Self { self }
        
        case byName = "Name"
        case byDateAdded = "Date Added"
    }

    @State private var currentSortMethod = SortMethod.byName

    var body: some View {
        ItemListView(sortMethod: currentSortMethod) // See child view implementation below
        .toolbar {
            ToolbarItem(placement: .principal) {
                Picker("Sort by", selection: $currentSortMethod) {
                    ForEach(SortMethod.allCases) { sortMethod in
                        Text(sortMethod.rawValue)
                    }
                }
            }
        }
    }
}

and the child view looks like:

struct ItemListView: View {
    
    @Environment(\.managedObjectContext) private var managedObjectContext
    @FetchRequest var items: FetchedResults<Item>
    
    init(sortMethod: ParentView.SortMethod) {
        let sortDescriptor: NSSortDescriptor
        switch sortMethod {
        case .byName:
            sortDescriptor = NSSortDescriptor(keyPath: \Item.name, ascending: true)
        case .byDateAdded:
            sortDescriptor = NSSortDescriptor(keyPath: \Item.dateAdded, ascending: true)
        }
        _items = .init(
            entity: Item.entity(),
            sortDescriptors: [sortDescriptor],
            predicate: nil,
            animation: .default
        )
    }
    
    var body: some View {
        List {
            ForEach(items) { item in
                SingleItemView(item)
            }
        }
    }
}

However, when I change the sorting option, the list doesn't animate the reordering (presumably due to the entire ItemListView being reconstructed. If I add .animation(.default) to ItemListView() in the parent view, the list animates when reordering, but also has weird animations when navigating back from other views. I can't seem to figure out where I might be able to add a withAnimation { } block. Or is there a better approach to this that's more natural to SwiftUI and thus allows some default animation?

Hl answered 17/9, 2020 at 22:5 Comment(0)
A
4

Binding can have attached animation, so try the following (or with any animation parameters you wish)

Picker("Sort by", selection: $currentSortMethod.animation())  // << here !!
Able answered 18/9, 2020 at 3:21 Comment(1)
I feel like I run into these retrospectively obvious answers quite a lot, how did you learn about this one specifically? Trying to learn how you learned :)Hl

© 2022 - 2025 — McMap. All rights reserved.