swiftnoob
swiftnoob

Reputation: 413

SwiftUI: Can only Edit the first item in a ForEach List

I have a list of bookmarks saved in Core Data. When i try to click on the edit button for a particular bookmark, only the values of the first bookmark on the list will fill up the textfields in the editing sheet no matter which bookmark i pick.

This is a macOS 10.15 app.

ContentView

@State private var showEditView = false



ForEach(vm.myBookmarks) { myBookmark in
                      Text(myBookmark.name)
                      Text(myBookmark.url)

                    
                    Button {
                        showEditView = true
                        } label: {
                        Image("pencil")
                        }
                        .sheet(isPresented: $showEditView) {
                                          EditBookmarkView(name: myBookmark.name, url: myBookmark.url, isVisible: $showEditView, bm: myBookmark)
                            }
                        }

EditBookmarkView

struct EditBookmarkView: View {
    
    @Environment(\.presentationMode)  var presentationMode
    @Binding var isVisible: Bool
    @ObservedObject var vm: EditBookmarkViewModel
    let name: String
    let url: String
    var bm: MyBookmarkViewModel
    
    init(name: String, url: String, isVisible: Binding<Bool>, bm: MyBookmarkViewModel) {
        self.bm = bm
        self.vm = EditBookmarkViewModel(bookmarkVM: bm)
        _isVisible = isVisible
        self.name = name
        self.url = url
    }
    
    
    var body: some View {
        TextField("Edit Name", text: $vm.name)

      
        Spacer()
        TextField("Edit url", text: $vm.url)
            
        Button("Update") {
            vm.save()
            
        }
           
        
      }

   }

Upvotes: 2

Views: 621

Answers (1)

Phil Dukhov
Phil Dukhov

Reputation: 88152

You create a sheet for each item in ForEach, and only one sheet can be represented at a time. So showEditView shows the sheet related to the first view in the hierarchy and it in turn captures the first item in your list.

Instead you can you use sheet which accepts item binding: sheet is presented when the item is not nil. You only need one such sheet, so add it to your list, not to each item.

When you finish editing, set selectedBookmark to nil to hide the sheet.

Full working example:

struct Bookmark: Identifiable {
    let id: Int
}

struct ContentView: View {

    @State private var selectedBookmark: Bookmark?
    let items = (0..<10).map { Bookmark(id: $0) }

    var body: some View {
        ForEach(items) { myBookmark in
            HStack {
                Text(String(describing: myBookmark))


                Button {
                    selectedBookmark = myBookmark
                } label: {
                    Image(systemName: "pencil").foregroundColor(.red)
                }
            }
        }
        .sheet(item: $selectedBookmark) { selectedBookmark in
            let _ = print(String(describing: selectedBookmark))
            Text(String(describing: selectedBookmark))
        }
    }
}

Upvotes: 4

Related Questions