'Unable to identify bug where final iteration (index) in ForEach does not update the view (SwiftUI)
Original question/post: Below is a snippet from an educational animals app for kids that is currently in beta testing. The below represents cards in a tab view that users can swipe, containing an image and the name of the animal. There is a feature within the app that allows users to turn off the names shown by default for each animal, where users are required to tap a button (Click here to reveal its name) to reveal the displayed animal's name.
The problem I am experiencing is that when users swipe to the end of the array (i.e., the last animal card) AND when the show names feature is off by default, it is transferring the name of the penultimate card to the last (i.e., the second to last card's name is being shown for the last card, but corrects itself when the name of the last card is tapped).
I have included screenshots to contextualise this a bit better. I simply cannot see why this is happening. I am relatively new to coding so will appreciate any support on this matter. Happy to clarify any bits I have not explained or refine my question if needed.Image1 Image2 Image3 Image4
Just to reiterate, the cards display the names perfectly until it is the final card (see images), where the name displayed is that of the preceding animal.
GeometryReader { geo in
TabView {
ForEach (0..<model.animals.count, id: \.self) { index in
// Condition to check selected index to match category selected
if selectedIndex == 0 {
if model.animals[index].category == "farm" {
ZStack (alignment: .topLeading) {
ZStack {
Rectangle()
.foregroundColor(.white)
VStack (spacing: 5) {
ImageContainer(image: model.animals[index].image, geo: geo)
Button {
revealName = model.animals[index].name
if model.animals[index].nameSound != "" {
if model.nameSoundIsOn != false {
playSound(sound: model.animals[index].nameSound, type: "mp3")
}
}
} label: {
if model.isRevealed == false {
HiddenNameText(revealName: revealName)
}
else {
NameText(name: model.animals[index].name)
}
}
.buttonStyle(.plain)
}
// Tap card to play animal sound feature
.onTapGesture {
if model.animals[index].sound != "" {
if model.soundIsOn != false {
playSound(sound: model.animals[index].sound, type: "mp3")
}
}
}
// Use of onAppear to stop playing sounds as cards are swiped
.onAppear {
revealName = "Click here to reveal its name"
audioPlayer?.stop()
/* playSound(sound: "swipe", type: "mp3") previous code for swipe card sound */
}
}
.frame(width: geo.size.width * 0.85, height: geo.size.height * 0.95, alignment: .center)
.clipped()
.cornerRadius(10)
.shadow(color: .black, radius: 10, x: -5, y: 5)
.tag(index)
SpeakerView(soundIsOn: model.soundIsOn)
}
}
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .never))
}
EDIT: Below I have provided a complete and simple minimal reproducible example (for the above) in the form of an integer-based flash cards app that can be swiped across, where when tapped displays numbers from 0-3. You can see that when on flash card three (displaying integer 2), it transfers across to card four. This occurs when tap to reveal is tapped for each card in order. The expected behaviour is that the integer for each card should only be displayed when tapped and there should be no transfer of integers from one card another. I am hoping this will reproduce the error for others on here to spot what is going wrong here or identify the bug for me.
import SwiftUI
struct IntCardView: View {
@State var isRevealed = false
@State var intHidden = "Tap to reveal"
var body: some View {
GeometryReader { geo in
TabView {
ForEach (0..<4) { index in
ZStack {
Rectangle()
.foregroundColor(.white)
VStack {
// Reveal integer feature within button
Button {
self.intHidden = "\(index)"
} label: {
Text(isRevealed ? "\(index)" : intHidden)
}
}
.onAppear {
self.intHidden = "Tap to reveal"
}
}
.frame(width: geo.size.width * 0.85, height: geo.size.height * 0.5, alignment: .center)
.clipped()
.cornerRadius(10)
.shadow(color: .black, radius: 10, x: -4, y: 4)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .never))
}
}
}
struct IntCardView_Previews: PreviewProvider {
static var previews: some View {
IntCardView()
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
