Home > other >  onTapGesture with CustomView not Refreshing child view
onTapGesture with CustomView not Refreshing child view

Time:05-01

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.

CodePudding user response:

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
    

CodePudding user response:

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()
          }
        }
      }
  }
  • Related