I am attempting to add a favorites feature to my app using a List( ) with sections. I have two arrays stored in an @EnvironmentObject which are iterated through to populate the favorites and other sections. When the arrays are modified, the content (button) moves as expected, but the action and formating does not change as expected.
The screenshots below demonstrate the issue:
1. Opening screen
2. Clicking on the heart icon moves the item, but the color is not updated
3. Clicking on the heart icon once it has moved behaves correctly, but adds the item to the favorites list a second time
The expected behavior would be that the item moves and the action and format are updated on the first press. Here is the code for the page I am using:
import SwiftUI
struct ContentView: View {
@EnvironmentObject var state: ApplicationState
var body: some View {
VStack{
List {
Section(header: Text("Favorites")) {
ForEach(state.favorites, id: \.self) { favorite in
HStack{
Button(
action: {
self.state.others.append(favorite)
self.state.favorites = self.state.favorites.filter {$0 != favorite}
self.sortItems()
},
label: {Image(systemName: "heart.fill").foregroundColor(.red)}
)
.id(favorite.id)
Text(favorite.name)
}
}
}
Section(header: Text("Others")) {
ForEach(state.others, id: \.self) { other in
HStack{
Button(
action: {
self.state.favorites.append(other)
self.state.others = self.state.others.filter {$0 != other}
self.sortItems()
},
label: {Image(systemName: "heart")}
)
.id(other.id)
Text(other.name)
}
}
}
}
}.onAppear(){
self.populateState()
}
}
func sortItems(){
self.state.favorites.sort {
$0.name < $1.name
}
self.state.others.sort {
$0.name < $1.name
}
}
func populateState(){
state.others.append(ListItem(id: 1, name: "One"))
state.others.append(ListItem(id: 2, name: "Two"))
state.others.append(ListItem(id: 3, name: "Three"))
state.others.append(ListItem(id: 4, name: "Four"))
state.others.append(ListItem(id: 5, name: "Five"))
}
}
A full minimal example is here: https://github.com/benbaran/swift-ui-favorites-list
Does anyone see anything I am doing wrong? Perhaps this is a bug?
Thanks!
Seems SwiftUI tries to update views as `move` when an item of exactly the same id is moved inside the data source of a List.
You may call it a bug, or one of the current limitations of SwiftUI.
Anyway, you can send a bug report using Apple's bug reporter.
A hacky workaround. Define different ids for favorites and others:
extension ListItem {
var favoriteId: String {
"favorite\(id)"
}
var otherId: String {
"other\(id)"
}
}
And use each id for each section:
var body: some View {
VStack{
List {
Section(header: Text("Favorites")) {
ForEach(state.favorites, id: \.favoriteId) { favorite in
//...
}
}
Section(header: Text("Others")) {
ForEach(state.others, id: \.otherId) { other in
//...
}
}
}
}.onAppear(){
self.populateState()
}
}
By the way, we cannot see your screenshots below, this site might have removed it.