You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI:ForEach循环内Button触发Toast弹窗重复问题求助

问题解决:单个按钮触发对应Toast弹窗

问题场景

基于《Fancy Toast Messages》实现Toast弹窗后,在配料列表的每个项添加存入Realm数据库的按钮,出现异常:点击任意按钮会触发列表中所有按钮对应的Toast弹窗,而非仅当前点击项的弹窗。

问题原因

所有按钮共用同一个@State private var toast: MessageToast?状态变量,当该变量被赋值时,所有绑定了这个变量的.toastView都会响应并弹出弹窗,导致所有Toast同时显示。

解决方案

方案1:抽分子视图(推荐)

将单个配料项的视图独立成子组件,让每个子视图拥有自己的Toast状态变量,确保每个按钮只触发自身对应的Toast。

独立配料项子视图代码

import SwiftUI

struct IngredientItemView: View {
    let item: String
    @State private var toast: MessageToast? = nil
    
    var body: some View {
        HStack {
            Text(item)
                .lineLimit(nil)
                .multilineTextAlignment(.leading)
                .font(.system(.body, design: .serif))
                .frame(minHeight: 50)
                .padding(.leading)
                .foregroundColor(.yellow) 
            Spacer()                        
            Button {
                // 此处添加存入Realm数据库的逻辑
                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: View {
    let drinks: Drinks

    var body: some View {
        Spacer()            
        Text("INGREDIENTS")
            .fontWeight(.bold)
            .modifier(TitleModifier())           
        ForEach(drinks.ingredients, id: \.self) { item in              
            VStack(alignment: .leading, spacing: 2) {                 
                IngredientItemView(item: item)
            }                
        }
    }
}

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

方案2:用字典管理独立状态

如果不想新增子视图,可以通过字典存储每个配料项对应的Toast状态,实现独立绑定:

struct DrinkDetailViewIngredients: View {
    let drinks: Drinks
    @State private var toastDict: [String: MessageToast?] = [:]

    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 {
                        // 此处添加存入Realm数据库的逻辑
                        toastDict[item] = MessageToast(
                            type: .success, 
                            title: "Item Added", 
                            message: "Item Added to shopping list."
                        )
                    } label: {
                        Image(systemName: "plus")
                            .foregroundColor(.yellow)
                            .imageScale(.large) 
                    }
                    .toastView(toast: $toastDict[item])
                    .buttonStyle(.borderless)                           
                }                        
            }                
        }
    }
}

说明

  • 方案1更符合SwiftUI视图组合的设计原则,代码结构清晰,便于后续维护和扩展。
  • 方案2适合不想新增子视图的场景,通过键值对实现每个配料项的状态独立。

内容的提问来源于stack exchange,提问作者brandon aho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 00:01:31