Home > Blockchain >  SwiftUI insert, delete, move and select with smooth animation
SwiftUI insert, delete, move and select with smooth animation

Time:01-16

I am planning to implement following features in the SwiftUI list - delete, insert, move and select.

With the existing list I am able to delete a row. But can't select a row does not work with List(selection: self.$selectedObject). When I hit edit it always enters into delete mode. And I comment the delete code nothing happens when I tap on edit button. This the first problem.

Also, selectedObject can it be moved to Model instead of keeping it with the ContentView?

Like UITableView, I am not able to get the insert green button. Is it like SwiftUI does not support the green insert button?

Overall trying to understand how the insert, delete, move and select functionality can work with the List SwiftUI.

Another problem I have noticed is that animation is very fast and not smooth when it enters into edit mode (with delete actions).

enter image description here


struct ContentView: View {
    
    @StateObject private var model = Model()
    
    @State var selectedObject: Locations?
    
    var body: some View {
        NavigationView {
            
            List(selection: self.$selectedObject) {
                ForEach(model.identifiableLocations) { location in
                    Text(location.name)
                }
                .onDelete(perform: delete(of:))
                
            }.listStyle(.plain)
                .navigationTitle("Places")
                .toolbar {
                    EditButton()
                    Button {
                        model.addLocation(name: "Test")
                    } label: {
                        Image(systemName: "plus")
                    }
                }
        }
    }
    
    func delete(of indexSet: IndexSet){
        indexSet.forEach { index in
            model.delete(itemAt: index)
        }
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().previewDevice(PreviewDevice(rawValue: "iPhone 14"))
    }
}

extension ContentView {
    @MainActor class Model: ObservableObject {
        @Published private(set) var identifiableLocations = [Locations(name: "USA"),
                                                Locations(name: "Switzerland")]
        
    }

}

extension ContentView.Model {
    func addLocation(name: String) {
        identifiableLocations.append(Locations(name: name))
    }
    
    func delete(itemAt index: Int) {
        identifiableLocations.remove(at: index)
    }
    
    
}

struct Locations {
    var name: String
}

extension Locations: Identifiable,Hashable {
    var id: String {
        return UUID().uuidString
    }
}

CodePudding user response:

  1. to make selection work, the list cells need a .tag(). This value is going into the selection var.

  2. yes, selectedObject can be moced to the view model as an additional @Published var

  3. SwiftUI List does not have an insert method, but your Add Button already does that.

  4. The animation is broke because your id in Location is not stable, but generated on each call by the computed var. id should be stable!

Here a running code with comments:

@MainActor
class ViewModel: ObservableObject {
    
    @Published private(set) var identifiableLocations = [
        Locations(name: "USA"),
        Locations(name: "Switzerland")
    ]
    
    // published selection var
    @Published var selectedObject: Locations?

    
    func addLocation(name: String) {
        identifiableLocations.append(Locations(name: name))
    }
    
    func delete(itemAt index: Int) {
        identifiableLocations.remove(at: index)
    }
    
    // new move func
    func move(fromOffsets: IndexSet, toOffset: Int) -> Void {
        identifiableLocations.move(fromOffsets: fromOffsets, toOffset: toOffset)
    }

}

struct Locations: Identifiable, Hashable {
    
    let id = UUID() // id has to stay stable
//    var id: String {
//        return UUID().uuidString
//    }

    var name: String
}


struct ContentView: View {
    
    @StateObject private var viewModel = ViewModel()
    
//    @State var selectedObject: Locations? // is now in viewmodel
    
    var body: some View {
        NavigationView {
            
            List(selection: $viewModel.selectedObject) {
                ForEach(viewModel.identifiableLocations) { location in
                    Text(location.name)
                        .tag(location) // this makes selction work
                }
                .onDelete(perform: delete(of:))
                
                .onMove(perform: viewModel.move)
                
            }
            .listStyle(.plain)
            .navigationTitle("Places")
            .toolbar {
                EditButton()
                Button {
                    viewModel.addLocation(name: "Test")
                } label: {
                    Image(systemName: "plus")
                }
            }
        }
    }
    
    func delete(of indexSet: IndexSet){
        indexSet.forEach { index in
            self.viewModel.delete(itemAt: index)
        }
    }
    
}
  • Related