2

Does anyone know how I can animate a sorting change to grid items while iterating off of the array index in a ForEach?

I am showing a grid of items (LazyVGrid) from an array and the user can change the sort order of these items. When the sort order changes, I'd like to animate the change in the grid. This would work great if I were to use code similar to the following:

            Button {
                withAnimation {
                    noteModel.sortMethod = noteModel.sortMethod.next()
                }
            } label: {
                Text("Change Sort Order")
            }

            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(self.notes, id: \.self) { note in
                        VStack {
                            Text(note.title)
                            Text(note.body)
                        }
                        .padding()
                    }
                }
            }

However, if my ForEach iterates off of the array index the change does not animate. So it won't animate if my code is similar to this:

            Button {
                withAnimation {
                    noteModel.sortMethod = noteModel.sortMethod.next()
                }
            } label: {
                Text("Change Sort Order")
            }

            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(self.notes.indices, id: \.self) { idx in
                        VStack {
                            Text(self.notes[idx].title)
                            Text(self.notes[idx].body)
                        }
                        .padding()
                    }
                }
            }

Why don't I just do it the first way? The reason is because I found a great resource for dynamically adjusting frame sizes for items inside a grid (found here: https://swiftui-lab.com/impossible-grids/), but that requires the use of array indices in order to work. It's pretty cool and I am hoping I don't have to choose one or the other (dynamic sizing or animate changes).

Thoughts or ideas would be greatly appreciated. Thanks!

3
  • 1
    It is not animated by indexes, because indexes are not changed after sorting (1,2,3... stay 1,2,3...), so nothing to animate. Commented Dec 24, 2021 at 16:49
  • This needs a Minimal, Reproducible Example. Commented Dec 24, 2021 at 17:05
  • Thank you Asperi! That makes complete sense now. I appreciate your help! Commented Dec 24, 2021 at 18:14

1 Answer 1

1

ForEach in SwiftUI isn’t the same as a for loop. Your data needs to be identifiable for structural identity to work and you certainly should not be using indices. Either implement the Identifiable protocol or tell ForEach what key path is a unique ID (or is a getter that creates one from other properties) in the item struct. Don’t ever use id:\.self.

You can learn more about structural identity in the video WWDC 2021 Demystify SwiftUI

Sign up to request clarification or add additional context in comments.

2 Comments

This, along with the comment from Asperi above, give some clarity to my issue. Thanks for the replies.
Adding a follow-up because with this understanding I was able to find a post that discussed the topic in some detail. It also helped me to discover a way to have access to an index and animate the array. The post is here: stackoverflow.com/questions/57244713/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.