I am trying to use the swipeActions SwiftUI modifier setup as displayed in the code below but the swipe action gets disabled as seen on this gif:
struct ContentView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
if viewModel.items.count > 0 {
ZStack {
List {
ForEach(viewModel.items, id: \.self) { item in
Text(item)
.swipeActions {
Button {
viewModel.removeAction(item: item)
} label: {
Text("Remove")
}
.tint(.orange)
}
}
}
}
} else {
ProgressView()
.foregroundColor(.accentColor)
.scaleEffect(2)
}
}
In the view model after the first swipe, I would reload the list from the API (the sample code just mocks a delay):
extension ContentView {
class ViewModel: ObservableObject {
@Published var items: [String]
init(items: [String]) {
self.items = items
}
func removeAction(item: String) {
if let index = items.firstIndex(where: { $0 == item }) {
items.remove(at: index)
}
let itemsSaved = items
items = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.items = itemsSaved
}
}
}
Expected behaviour: the reloaded rows do not have a space view at the beginning of each row, and the rows can be swiped as before.
Actual behaviour: each rows has a space view at the beginning of the row, you cannot swipe the rows as before.
I created a sample project also: code and additional video.
Any idea if there is workaround?
Thanks.
I am still not sure why your implementation is causing this behavior, other than that you are completely switching between two separate views(Zstack
vs. ProgressView
). My suspicion is that the change back and forth is simply putting the List
into some weird state. The fix, however, is simple; put the conditional inside of the ZStack
:
struct ContentView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
ZStack {
// Move the conitional inside of the ZStack.
// Always use .isEmpty for this sort of test. Faster and
// less resources than count
if !viewModel.items.isEmpty {
List {
// I made Item an Identifiable struct. Deleting items
// identified as .self can lead to issues in a ForEach
ForEach(viewModel.items) { item in
Text(item.name)
.swipeActions {
Button {
viewModel.removeAction(item: item)
} label: {
Text("Remove")
}
.tint(.orange)
}
}
}
} else {
Text("Progress View")
}
}
}
}
extension ContentView {
class ViewModel: ObservableObject {
@Published var items: [Item]
init(items: [Item]) {
self.items = items
}
func removeAction(item: Item) {
if let index = items.firstIndex(where: { $0 == item }) {
items.remove(at: index)
}
let itemsSaved = items
items = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.items = itemsSaved
}
}
}
}
// I made this as a data model.
struct Item: Identifiable, Hashable {
var id = UUID()
var name: String
}