Home > Software design >  SwiftUI : How I can use confirmationDialog for each row of a list?
SwiftUI : How I can use confirmationDialog for each row of a list?

Time:04-21

import SwiftUI

struct MemoListView: View {
    
    let folder : FolderModel
    @State private var showActionSheet : Bool = false
    @EnvironmentObject var vm : FolderListViewModel
    
    var body: some View {
        ZStack {
            if folder.memo.count == 0 {
                NoMemoView()
            } else {
                List {
                    ForEach(folder.memo) { memo in
                        MemoRowView(memo: memo, folder: self.folder)
                            .onLongPressGesture {
                                self.showActionSheet.toggle()
                            }
                            .confirmationDialog(Text("Option"), isPresented: $showActionSheet) {
                                Button(role : .destructive, action: {
                                    vm.deleteMemo(folder: folder, memo: memo)
                                }, label: {
                                    Text("Delete")
                                        .foregroundColor(.red)
                                })
                            }
                    }
                        .listRowSeparator(.hidden)
                }
                .listStyle(.plain)
            }
        }
        .navigationTitle("Memos in '\(folder.folderName)'")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                HStack {
                    NavigationLink(destination: {
                        NewMemoView(folder: self.folder)
                    }, label: {
                        Image(systemName: "plus")
                    })
                    NavigationLink(destination: {
                        
                    }, label: {
                        Image(systemName: "gear")
                    })
                }
            }
        }
    }
}

Hi! Please check my code above. I want to apply actionSheet(confirmationDialog) to each of row in list but I think the list can't recognize which row is selected.

If I tried to delete row3, it just delete only row1

I don't know how I can handle this situation.

Thanks!

CodePudding user response:

As per your comments to the original post, .confirmationDialog only has an isPresented binding available, rather than an object-based one as with sheet, fullScreenCover, etc.

One workaround I've used quite successfully is to move the confirmationDialog, and the boolean state flag that drives it, into a child view. This could be MemoRowView itself, or you could keep that view as the presentation component only and wrap it in an interactive view that called MemoRowView and added the interactive elements, e.g.

// MemoListView
List {
  ForEach(folder.memo) { memo in
    MemoListItem(memo: memo, folder: self.folder)
  }
.listStyle(.plain)

This does mean injecting quite a lot of domain knowledge into the list item, so it'd need its own @EnvironmentObject reference, etc., but that could be lived with.

Alternatively, you could keep the code that deletes the object in the parent view, and just keep the confirmation in the child view:

struct MemoListItem: View {
  var memo: MemoModel
  var folder: FolderModel
  var onDelete: () -> Void

  @State private var showDeleteConfirmation: Bool = false

  init(memo: MemoModel, folder: FolderModel, onDelete: @escaping () -> Void) {
    self.memo = memo
    self.folder = folder
    self.onDelete = onDelete
  }

  var body: some View {
    // rest of row setup omitted for brevity
    .confirmationDialog(Text("Option"), isPresented: $showDeleteConfirmation) {
      Button("Delete", role: .destructive, action: onDelete)
    }
  }
}

// MemoListView
List {
  ForEach(folder.memo) { memo in
    MemoListItem(memo: memo, folder: self.folder, onDelete: {
      vm.deleteMemo(folder: folder, memo: memo)
    })
  }
}

CodePudding user response:

An explanation of what I wrote in my comment :

struct MemoListView: View {   
    @EnvironmentObject var vm : FolderListViewModel
    // Updated folder so as it seems it is extracted from your 
    // view model
    var folder : FolderModel {
        vm.folder
    }
    @State private var showActionSheet : Bool = false
    // This is the way to know which memo to delete
    @State var memoToDelete: Memo?
    
    var body: some View {
        ZStack {
            if folder.memo.count == 0 {
                NoMemoView()
            } else {
                List {
                    ForEach(folder.memo) { memo in
                        MemoRowView(memo: memo, folder: self.folder)
                            .onLongPressGesture {
                                // Save the memo on the current row
                                memoToDelete = memo
                                self.showActionSheet.toggle()
                            }
                            .confirmationDialog(Text("Option"), isPresented: $showActionSheet) {
                                Button(role : .destructive, action: {
                                    // Delete the saved memo
                                    if let memo = memoToDelete {
                                        vm.deleteMemo(folder: folder, memo: memo)
                                    }
                                }, label: {
                                    // Here to show the memo id before delete
                                    if let memo = memoToDelete {
                                        Text("Delete \(memo.id)")
                                            .foregroundColor(.red)
                                    }
                                })
                            }
                    }
                    .listRowSeparator(.hidden)
                }
                .listStyle(.plain)
                
            }
        }
        .navigationTitle("Memos in '\(folder.folderName)'")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                HStack {
                    NavigationLink(destination: {
                        NewMemoView(folder: self.folder)
                    }, label: {
                        Image(systemName: "plus")
                    })
                    NavigationLink(destination: {
                        
                    }, label: {
                        Image(systemName: "gear")
                    })
                }
            }
        }
    }
}

Note : I changed from the comment and kept the dialog on each row. It is not the best way to do it.

  • Related