SwiftUI动态占位符实现与ForEach动态数据报错问题咨询
我来帮你搞定这两个SwiftUI的问题哈~
问题1:在ForEach遍历数组时实现动态自定义TextField占位符
首先你代码里有个小笔误:内层ForEach的参数是idx,但判断的时候写成了index,先修正这个。然后要实现自定义占位符,咱们可以用ZStack把占位文本和TextField叠在一起,利用数组元素的空值状态来控制占位符的显示,同时把TextField的值绑定到数组对应位置,这样每个输入框都能动态判断要不要显示占位符:
ForEach(self.ListeEquip.indices, id: \.self) { item in ForEach(self.ListeJoueurs[item].indices, id: \.self){ idx in ZStack(alignment: .leading) { // 当当前数组元素为空时,显示红色占位文本 if self.ListeJoueurs[item][idx].isEmpty { Text("Placeholder") .foregroundColor(.red) .padding(.horizontal, 8) // 和TextField的内边距对齐,避免错位 } // 绑定到数组对应位置的TextField,空值时就会显示上面的占位符 TextField("", text: self.$ListeJoueurs[item][idx]) .padding(8) .border(Color.gray) } } }
这样就完美适配了ForEach遍历多组数组的场景,每个TextField的占位符都会根据自己的输入状态动态显示~
问题2:点击按钮添加元素时ForEach报错
这个报错的原因很明确:你用了基于数组索引的ForEach(self.ListeJoueurs[EquipName].indices),SwiftUI会把这种索引当成“常量标识”,当你点击按钮添加元素时,数组的元素数量变化了,索引的对应关系就乱了,所以会报错。解决办法有两种,推荐第一种:
方案1:给数组元素加唯一标识(推荐)
把字符串包装成一个遵循Identifiable协议的结构体,这样每个元素都有一个稳定的唯一ID,即使数组数量变化,SwiftUI也能准确识别每个元素:
// 先定义一个可识别的玩家结构体,每个实例都有唯一的UUID struct Joueur: Identifiable { let id = UUID() var name: String } // 修改你的State变量类型,换成Joueur数组 struct EquipView: View { @State var ListeJoueurs = [ [Joueur(name: "saoul"), Joueur(name: "Remi")], [Joueur(name: "Paul"), Joueur(name: "Kevin")] ] @State var ListeEquip:[String] = [ "Rocket", "sayans" ] var body: some View { VStack { ForEach(self.ListeEquip.indices) { item in BulleEquip(EquipName: item, ListeJoueurs: self.$ListeJoueurs, ListeEquip: self.$ListeEquip) } } } } struct BulleEquip: View { var EquipName = 0 @Binding var ListeJoueurs :[[Joueur]] @Binding var ListeEquip :[String] var body: some View { VStack{ Text("Équipe \(EquipName+1)") VStack { // 现在可以直接遍历绑定的数组元素,不用依赖索引 ForEach($ListeJoueurs[EquipName]) { $joueur in // 这里如果需要索引,可以通过firstIndex获取 let index = ListeJoueurs[EquipName].firstIndex(where: { $0.id == joueur.id })! + 1 ListeJoueurView(joueur: $joueur, JoueurIndex: index) } HStack{ Button(action: { self.ListeJoueurs[self.EquipName].append(Joueur(name: "")) }){ Text("Ajouter joueur") } } } } } } struct ListeJoueurView: View { @Binding var joueur: Joueur var JoueurIndex = 0 var body: some View { HStack{ Text("Joueur \(JoueurIndex)") TextField("Nom du joueur", text: $joueur.name) } } }
方案2:给ForEach显式指定ID(临时解决)
如果你不想包装结构体,也可以修改ForEach的写法,遍历数组元素的同时指定id: \.self(但注意如果有重复的字符串,可能会出问题):
在BulleEquip里把ForEach改成:
ForEach(ListeJoueurs[EquipName].enumerated(), id: \.element) { (index, name) in ListeJoueurView(EquipNamed: self.EquipName, JoueurIndex: index, ListeJoueurs: self.$ListeJoueurs, ListeEquip: self.$ListeEquip) }
不过这种方案不如第一种稳定,还是更推荐用Identifiable的方式哦~
内容的提问来源于stack exchange,提问作者Nicolas M
相关产品推荐
相关产品推荐

