SwiftUI实现可多选自定义网格项:选中状态切换与数据收集问题
SwiftUI可选择网格项问题解决
问题描述
实现可选择网格项时遇到两个问题:
- 点击项无法切换背景色与文字颜色
- 选中的项无法添加到
selectedSports状态集合中
使用WrappingStack库布局,原代码如下(略)
解决方案
1. 改造SportItem组件
让组件支持绑定选中状态,根据状态切换样式,并添加点击交互:
import SwiftUI struct SportItem: View { var sport: Sport // 绑定父视图的选中状态 @Binding var isSelected: Bool // 定义两种状态的样式参数 private let normalBg = Color(red: 0.404, green: 0.314, blue: 0.643) private let selectedBg = Color.white private let normalText = Color.white private let selectedText = Color(red: 0.404, green: 0.314, blue: 0.643) var body: some View { HStack{ Text(sport.emoji) Text(" "+sport.name) .bold() } .font(.system(size: 14)) .padding(8) // 根据选中状态切换背景色 .background(isSelected ? selectedBg : normalBg) // 根据选中状态切换文字颜色 .foregroundColor(isSelected ? selectedText : normalText) .cornerRadius(15) .shadow(color: .black, radius: 3) // 点击触发选中状态切换 .onTapGesture { isSelected.toggle() } } } struct SportItem_Previews: PreviewProvider { static var previews: some View { SportItem(sport: Sport(id: 1, emoji: "⚽️", name: "足球"), isSelected: .constant(false)) } }
2. 更新父视图SelectSport
遍历体育项时判断选中状态,绑定子组件并更新selectedSports集合:
import SwiftUI import WrappingStack struct SelectSport: View { var event: Event @State private var eventForm = Event(id: Int(), name: "", place: "", description: "",date: Date(), beginTime: 1, endTime: 2, img: "", sports: Set<Sport>()) @State var selectedSports: Set<Sport> = Set<Sport>() @EnvironmentObject var modelData: ModelData var sportsList: [Sport] { modelData.sports } var body: some View { VStack{ Text("Which sports do you want to include?") Text(event.name) WrappingHStack { ForEach(sportsList){ sport in // 判断当前项是否已被选中 let isSelected = selectedSports.contains(sport) SportItem(sport: sport, isSelected: .constant(isSelected)) .padding(4) .onTapGesture { if isSelected { selectedSports.remove(sport) } else { selectedSports.insert(sport) } } } } // 可选:调试用,显示选中项数量 Text("已选中:\(selectedSports.count)项") .padding() } .navigationTitle("Select Sports") .navigationBarItems(trailing: NavigationLink(destination: DatePickerEvent(event: eventForm)){ Text("Next") } ) } } struct SelectSport_Previews: PreviewProvider { static var previews: some View { SelectSport(event: Event(id: 1, name: "", place: "", description: "",date: Date(), beginTime: 1, endTime: 2, img: "", sports: Set<Sport>())) .environmentObject(ModelData()) } }
关键注意点
Sport必须遵循Hashable:因为要存入Set集合,需确保Sport实现Hashable协议(基于id实现即可):struct Sport: Identifiable, Hashable { var id: Int var emoji: String var name: String func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: Sport, rhs: Sport) -> Bool { lhs.id == rhs.id } }- 原代码中
NavigationLink的目标视图创建了新Event实例,建议改为传递eventForm,方便后续将选中的运动项同步到表单中。
内容的提问来源于stack exchange,提问作者Diego Enrique Molina Sanchez
相关产品推荐
相关产品推荐

