Swift中如何在ForEach循环里仅显示数组内指定的两个元素?
解决方案:在PlayingCreatures视图中仅展示两个生物元素
根据你的需求,这里提供几种实用实现方式,可根据展示逻辑选择:
方式一:固定展示数组前两个元素
直接对data.creatures做切片操作,取前2个元素传入ForEach即可:
struct PlayingCreatures: View { @EnvironmentObject var data : CreatureZoo var body: some View { VStack { ZStack { // 取数组前2个元素 ForEach(data.creatures.prefix(2)) { creature in Text(creature.emoji) .resizableFont() .offset(creature.offset) .rotationEffect(creature.rotation) .animation(.spring(response: 0.5, dampingFraction: 0.5), value: creature.rotation) .animation(.default.delay(data.indexFor(creature) / 10), value: creature.offset) } } .onTapGesture { data.randomizeOffsets() } } } }
方式二:随机展示两个不同元素
如果需要每次打开视图或点击时切换展示的生物,可按以下两种方式实现:
方案A:在视图内直接处理
通过计算属性生成随机子数组,配合状态变量触发刷新:
struct PlayingCreatures: View { @EnvironmentObject var data : CreatureZoo @State private var refreshTrigger = false // 生成随机两个生物的计算属性 var randomTwoCreatures: [Creature] { guard data.creatures.count >= 2 else { return data.creatures } let _ = refreshTrigger // 依赖状态变量,触发重新计算 return Array(data.creatures.shuffled().prefix(2)) } var body: some View { VStack { ZStack { ForEach(randomTwoCreatures) { creature in Text(creature.emoji) .resizableFont() .offset(creature.offset) .rotationEffect(creature.rotation) .animation(.spring(response: 0.5, dampingFraction: 0.5), value: creature.rotation) .animation(.default.delay(data.indexFor(creature) / 10), value: creature.offset) } } .onTapGesture { data.randomizeOffsets() refreshTrigger.toggle() // 点击时切换随机生物 } } } }
方案B:在CreatureZoo中封装方法(符合MVVM架构)
先在CreatureZoo扩展中添加生成随机子数组的方法:
extension CreatureZoo { // 新增:获取随机两个生物 func randomTwoCreatures() -> [Creature] { guard creatures.count >= 2 else { return creatures } return Array(creatures.shuffled().prefix(2)) } // 原有方法保留 func randomizeOffsets() { for index in creatures.indices { creatures[index].offset = CGSize(width: CGFloat.random(in: -200...200), height: CGFloat.random(in: -200...200)) creatures[index].rotation = Angle(degrees: Double.random(in: 0...720)) } } func synchronizeOffsets() { let randomOffset = CGSize(width: CGFloat.random(in: -200...200), height: CGFloat.random(in: -200...200)) for index in creatures.indices { creatures[index].offset = randomOffset } } func indexFor(_ creature: Creature) -> Double { if let index = creatures.firstIndex(where: { $0.id == creature.id }) { return Double(index) } return 0.0 } }
再在视图中调用该方法:
struct PlayingCreatures: View { @EnvironmentObject var data : CreatureZoo @State private var refreshTrigger = false var body: some View { VStack { ZStack { ForEach(data.randomTwoCreatures()) { creature in Text(creature.emoji) .resizableFont() .offset(creature.offset) .rotationEffect(creature.rotation) .animation(.spring(response: 0.5, dampingFraction: 0.5), value: creature.rotation) .animation(.default.delay(data.indexFor(creature) / 10), value: creature.offset) } } .onTapGesture { data.randomizeOffsets() refreshTrigger.toggle() } } .id(refreshTrigger) // 通过id触发视图刷新,重新获取随机生物 } }
注意事项
- 由于
Creature遵循Identifiable协议,切片或子数组可直接传入ForEach,无需额外指定ID。 - 所有方案都处理了数组元素不足2个的情况,避免崩溃。
内容的提问来源于stack exchange,提问作者Frank Reichard
相关产品推荐
相关产品推荐

