skip to Main Content

I’m having some issues working out an "onTapGesture" modifier with a custom view.

struct CheckView: View {
    @State var isChecked: Bool = false
    
    var title: String
    var unitPrice: String
    
    var body: some View {
       Button(action: toggle){
           HStack{
               Image(systemName: isChecked ? "checkmark.square": "square")
               HStack {
                   Text(title)
                   Spacer()
                   Text("- $(unitPrice)")
                       .frame(width: 65, alignment: .leading)
               }
               .foregroundColor(.black)
               Spacer()
           }
       }
    }
    
    func toggle() {
        isChecked = !isChecked
    }

The custom view above is a Checkbox row view. When you click on the row, it basically just toggles the sf-symbol from a checked-box to an empty box.

In the parent view, where I’m creating this, I pull from a Realm DB to populate items in a list view.

@ObservedResults(Item.self) var items
    
@StateObject private var viewModel = ViewModel()

List {
    ForEach(items) { item in
        CheckView(title: item.itemDescription, unitPrice: String(format: "%.2f", item.unitPrice))
            .onTapGesture {
                viewModel.updateSelectedItems(item: item)
            }
    }
}

//ViewModel Function
@Published var selectedItems: [Item] = []
        
func updateSelectedItems(item: Item) {
    if (selectedItems.contains(item)) {
        selectedItems.removeAll(where: { $0 == item })
    }
    else {
        selectedItems.append(item)
    }
}

What I’m trying to do is use the tap gesture to call the function in the viewmodel so that I can get a running list of what items have been tapped. I can confirm that the ViewModel code is working as it should (I have a debug alert that shows that the Published array is updating) but what’s happening is the CheckView does not refresh, as in, when tapping a row, the checkbox sf-symbol doesn’t toggle as it should. It looks like it’s doing nothing.

I am suspecting this is some sort of conflict between the Button on the CheckView and the onTapGesture on the parent view but am not sure.

If you have any ideas, it would be much appreciated.

Thanks in advance.

2

Answers


  1. you could try the following approach passing the Item and the ViewModel to CheckView

     struct CheckView: View {
         @EnvironmentObject var viewModel: ViewModel
         
        @State var item: Item
        @State var isChecked: Bool = false
        
        var body: some View {
            Button(action: {
                viewModel.updateSelectedItems(item: item)
                isChecked.toggle()
            }){
                HStack{
                    Image(systemName: isChecked ? "checkmark.square": "square")
                    HStack {
                        Text(item.itemDescription)
                        Spacer()
                        Text("- $(String(format: "%.2f", item.unitPrice))")
                            .frame(width: 65, alignment: .leading)
                    }
                    .foregroundColor(.black)
                    Spacer()
                }
            }
            .onAppear {
                isChecked = viewModel.selectedItems.contains(where: {$0.id == item.id})
            }
        }
    }
    

    and use it like this, without the onTapGesture :

    List {
        ForEach(items) { item in
            CheckView(item: item)
        }
    }.environmentObject(viewModel)  // <-- here
        
    
    Login or Signup to reply.
  2. Based on the answer above, if you pass the viewModel into the CheckView, could you not just check if the item is in the selectedItems array?

    struct CheckView: View {
      @EnvironmentObject var viewModel: ViewModel
     
      @State var item: Item
      var isSelected: Bool {
        viewModel.selectedItems.containts(item)
      }
    
      var body: some View {
        Button(action: {
          viewModel.updateSelectedItems(item: item)
        }) {
          HStack {
            Image(systemName: isSelected ? "checkmark.square": "square")
              HStack {
                Text(item.itemDescription)
                Spacer()
                Text("- $(String(format: "%.2f", item.unitPrice))")
                  .frame(width: 65, alignment: .leading)
                }
                .foregroundColor(.black)
                Spacer()
              }
            }
          }
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search