1

I have a rather strange problem with animation on adding elements to list in SwiftUI. I have a simple model class:

struct ShoppingList: Identifiable, Equatable {
    let id: UUID
    var name: String
    var icon: String
    var items: [Item]
    
    init(id: UUID = UUID(), name: String, icon: String, items: [Item] = []) {
        self.id = id
        self.name = name
        self.icon = icon
        self.items = items
    }
    
    static func == (lhs: ShoppingList, rhs: ShoppingList) -> Bool {
        return lhs.id == rhs.id
    }
}

extension ShoppingList {
    struct Item: Identifiable, Equatable {
        let id: UUID
        var name: String
        
        init(id: UUID = UUID(), name: String) {
            self.id = id
            self.name = name
        }
        
        static func == (lhs: Item, rhs: Item) -> Bool {
            return lhs.id == rhs.id
        }
    }
}

When I pass single ShoppingList object as binding to the view, adding animation basically doesn't happen at all:

No animation

Here is code of my view:

struct ShoppingListDetailView: View {
    @Binding var shoppingList: ShoppingList
    @State private var newItemName = ""
    
    var body: some View {
        List {
            ForEach($shoppingList.items) { $item in
                Text(item.name)
            }
            HStack {
                TextField("Add item", text: $newItem)
                Button(action: addNewItem) {
                    Image(systemName: "plus.circle.fill")
                }
                .disabled(newItemName.isEmpty)
            }
        }
        .navigationTitle(shoppingList.name)
    }
    
    private func addNewItem() {
        withAnimation {
            let newItem = ShoppingList.Item(name: newItemName)
            shoppingList.items.append(newItem)
        }
    }
}

And here is code of a parent view:

struct ShoppingListsView: View {
    @Binding var shoppingLists: [ShoppingList]

    var body: some View {
        List {
            ForEach($shoppingLists) { $list in
                NavigationLink(destination: ShoppingListDetailView(shoppingList: $list)) {
                    ShoppingListItemView(shoppingList: $list)
                }
            }
        }
        .navigationTitle("Shopping List")
    }
}

But once I pass whole list of ShoppingList objects and index for a particular one, everything works as expected:

Proper animation

Code with passing list to the view looks like that:

struct ShoppingListDetailView: View {
    @Binding var shoppingList: [ShoppingList]
    var index: Int
    @State private var newItemName = ""
    
    var body: some View {
        List {
            ForEach($shoppingList[index].items) { $item in
                Text(item.name)
            }
            HStack {
                TextField("Add item", text: $newItem)
                Button(action: addNewItem) {
                    Image(systemName: "plus.circle.fill")
                }
                .disabled(newItemName.isEmpty)
            }
        }
        .navigationTitle(shoppingList[index].name)
    }
    
    private func addNewItem() {
        withAnimation {
            let newItem = ShoppingList.Item(name: newItemName)
            shoppingList[index].items.append(newItem)
        }
    }
}

And of course parent view:

struct ShoppingListsView: View {
    @Binding var shoppingLists: [ShoppingList]

    var body: some View {
        List {
            ForEach($shoppingLists) { $list in
                NavigationLink(destination: ShoppingListDetailView(shoppingList: $shoppingLists, index: shoppingLists.firstIndex(of: list)!)) {
                    ShoppingListItemView(shoppingList: $list)
                }
            }
        }
        .navigationTitle("Shopping List")
    }
}

I'm new to Swift (not to mention SwiftUI) and I have no idea what might be wrong here. Any ideas?

ostojan
  • 608
  • 1
  • 7
  • 17

0 Answers0