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
相关产品推荐
相关产品推荐

