Dovizu
Dovizu

Reputation: 417

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

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?

Upvotes: 8

Views: 2209

Answers (1)

Asperi
Asperi

Reputation: 257703

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

Picker("Sort by", selection: $currentSortMethod.animation())  // << here !!

Upvotes: 4

Related Questions