Home > Net >  Button in ForEach Loop triggers multiple times on execution
Button in ForEach Loop triggers multiple times on execution

Time:01-24

I have created a message toast type alerts following Fancy Toast Messages

Then I created a view that loops through a json file of ingredients

[
    {
        "id" : 1,
        "name": "Spicy Watermelon Mint Agua Fresca",
        "headline": "A refreshing blend of fruity flavor with a blast of spice.",
        "image": "Drink1",
        "equipment": ["Blender", "Strainer"],
        "servingsize": 4,
        "drinkware": "Pitcher",
        "preptime": 10,
        "difficulty": 1.5,
        "rating": 2,
        "category": ["Agua Fresca"],
        "ingredients": [
            "2 cups cold water",
            "2 cups watermelon (rind removed), seeded and chopped",
            "2 tbsp granulated sugar",
            "1 tbsp lime juice",
            ".25 cup mint leaves",
            ".5 jalapeño, roughly chopped",
            "Ice cubes or crushed ice (optional)"
        ],
        "instructions": [
            "Add the ingredients into a blender.",
            "Blend ingredients until mixture is smooth.",
            "Pour mixture through a fine-mesh sieve into a large pitcher, forcing through most of the pulp.",
            "Let chill before serving.",
            "Serve and enjoy!"
        ],
        "shoppinglist": [
            "Watermelon",
            "Granulated Sugar",
            "Lime Juice",
            "Mint Leaves",
            "Jalapeño"
        ]
    }
]

Data model

struct Drinks: Codable, Identifiable, Comparable  {
    static func < (lhs: Drinks, rhs: Drinks) -> Bool {
        lhs.name < rhs.name
    }
    
    
    var id: Int
    var name: String
    var headline: String
    var image: String
    var equipment: [String]
    var servingsize: Int
    var drinkware: String
    var preptime: Int
    var difficulty: Double
    var rating: Int
    var category: [String]
    var ingredients: [String]
    var instructions: [String]
    var shoppinglist: [String]
    
    
}



All is displaying and working correctly.

The issue resides in the view that showing this. I am adding a button that adds the item that is clicked on to a realm database. That aspect is working on a one to one relationship.

What is displaying or exhibiting undesired behavior is when you click on the plus sign its triggering the message alert for every button that is in the stack.

I am not sure how to separate them out to a 1x1 event with the button so after each click it only fires the event once instead of one time for each button that was added to the view for each item in the ingredients list.

Here is the view

import SwiftUI

struct DrinkDetailViewIngredients: View {
  let drinks: Drinks
  @State private var toast: MessageToast? = nil

  var body: some View {
    Spacer()            
    Text("INGREDIENTS")
      .fontWeight(.bold)
      .modifier(TitleModifier())           
    ForEach(drinks.ingredients, id: \.self) { item in              
      VStack(alignment: .leading, spacing: 2) {                 
        HStack {
          Text(item)
            .lineLimit(nil)
            .multilineTextAlignment(.leading)
            .font(.system(.body, design: .serif))
            .frame(minHeight: 50)
            .padding(.leading)
            .foregroundColor(.yellow) 
          Spacer()                        
          Button {
            toast = MessageToast(
              type: .success, 
              title: "Item Added", 
              message: "Item Added to shopping list."
            )
          } label: {
            Image(systemName: "plus")
              .foregroundColor(.yellow)
              .imageScale(.large) 
          }
            .toastView(toast: $toast)
            .buttonStyle(.borderless)                           
        }                        
      }                
    }
  }
}

struct DrinkDetailViewIngredients_Previews: PreviewProvider {
  static var previews: some View {
    DrinkDetailViewIngredients(drinks: drinks[0])
  }
}

This is what happens when you click the plus sign and the message alert is triggered.

Message Alert Duplicates

CodePudding user response:

I figured out the solution to the issue. I embedded the ForEach Loop in a VStack and then moved the message alert to that VStack versus having the message alert on the button itself. Here is the final code.


import SwiftUI

struct DrinkDetailViewIngredients: View {
    //MARK: - PROPERTIES
    
    let drinks: Drinks
    @State private var toast: MessageToast? = nil
    
    //MARK: - FUNCTIONS

    //MARK: - BODY

    var body: some View {
        

                
            // Ingredients
            
            Spacer()
            
            Text("INGREDIENTS")
                .fontWeight(.bold)
                .modifier(TitleModifier())
            
        VStack {
            ForEach(drinks.ingredients, id: \.self) { item in
                    
                        VStack(alignment: .leading, spacing: 2) {

                        
                            HStack {
                                Text(item)
                                    .lineLimit(nil)
                                    .multilineTextAlignment(.leading)
                                    .font(.system(.body, design: .serif))
                                    .frame(minHeight: 50)
                                    .padding(.leading)
                                    .foregroundColor(Color(.yellow)
                                
                                Spacer()
                                
                                
                                VStack {
                                    Button {
                                        
                                            toast = MessageToast(type: .success, title: "Item Added", message: "Item Added to shopping list.")
                                       
                                    } label: {
                                        Image(systemName: "plus")
                                            .foregroundColor(Color(.yellow))
                                            .imageScale(.large)
                                        
                                    } //:END OF BUTTON
                                    .buttonStyle(.borderless)
                                    
                                } //:END OF VSTACK
                            } //:END OF HSTACK
                            
                        } //: END OF VSTACK INGREDIENTS

            }//: END OF LOOP INGREIDENTS
        } //: END OF OUTTER VSTACK
        .toastView(toast: $toast)
    } //: END OF BODY
} //: END OF STRUCT

//MARK: - PREVIEW

struct DrinkDetailViewIngredients_Previews: PreviewProvider {
    static var previews: some View {
        DrinkDetailViewIngredients(drinks: drinks[0])
    }
}
  • Related