I have a ScrollView that contains pinned items in the top and unpinned items in the bottom.
______________
| Item 1 |
| Item 2 |
| Item 3 |
|--------------|
| Item 4 |
| Item 5 |
| Item 6 |
| Item 7 |
| Item 8 |
|______________|
The Scroll view is populated by two collections (pinnedItems and unpinnedItems) using two ForEach-clauses in the ScrollView.
var pinnedItems = ["Item 1", "Item 2", "Item 3"]
var unpinnedItems = ["Item 4", "Item 5", "Item 6", "Item 7", "Item 8"]
ScrollView {
ForEach(pinnedTasks) { task in
NavigationLink(value: NavigationPath.task(task)) {
TaskCardView(task: task, onPinning: {
withAnimation {
togglePinnedTask(task: task)
}
})
.padding(.bottom)
}.buttonStyle(.plain)
}
Divider()
.frame(minHeight: 3)
.background(.gray)
.padding(.bottom)
ForEach(unPinnedTasks) { task in
NavigationLink(value: NavigationPath.task(task)) {
TaskCardView(task: task, onPinning: {
withAnimation {
togglePinnedTask(task: task)
}
})
.padding(.bottom)
}.buttonStyle(.plain)
}
}
The togglePinnedTask() function removes the task from one collection and inserts it into the other.
The automatic animation just fades the task cards. I would like to make them "fly" to their new position. What it the approach to do this?
You can use matchedGeometryEffect
. Here is a simple example:
struct ContentView: View {
@State private var pinnedItems = ["Item 1", "Item 2", "Item 3"]
@State private var unpinnedItems = ["Item 4", "Item 5", "Item 6", "Item 7", "Item 8"]
@Namespace var ns
var body: some View {
VStack {
ScrollView {
VStack {
ForEach(pinnedItems, id: \.self) { item in
Text(item)
.matchedGeometryEffect(id: item, in: ns)
}
ForEach(unpinnedItems, id: \.self) { item in
Text(item)
.matchedGeometryEffect(id: item, in: ns)
}
}
}
// as an example to trigger the animation
Button("Change Pins") {
withAnimation {
pinnedItems.append(unpinnedItems.removeLast())
}
}
}
}
}
Note that in both matchedGeometryEffect
s and the ForEach
es, I have used the item
itself as the id. In your real code, your items probably conform to Identifiable
and you should use their id
property instead.