struct ContentView: View {
@State var cardText: String = "Hello"
var body: some View {
self.cardText = "Goodbye" // <<< This mutation is no good.
return Text(cardText)
.onTapGesture {
self.cardText = "World"
}
}
}
Here I'm modifying a @State
variable within the body of the body
view. The problem is that mutations to @State
variables cause the view to update, which in turn call the body
method on the view. So, already in the middle of a call to body
, another call to body
initiated. This could go on and on.
On the other hand, a @State
variable can be mutated in the onTapGesture
block, because it's asynchronous and won't get called until after the update is finished.
For example, let's say I want to change the text every time a user taps the text view. I could have a @State
variable isFlipped
and when the text view is tapped, the code in the gesture's block toggles the isFlipped
variable. Since it's a special @State
variable, that change will drive the view to update. Since we're no longer in the middle of a view update, we won't get the "Modifying state during view update" warning.
struct ContentView: View {
@State var isFlipped = false
var body: some View {
return Text(isFlipped ? "World" : "Hello")
.onTapGesture {
self.isFlipped.toggle() // <<< This mutation is ok.
}
}
}
For your FlashcardView
, you might want to define the card outside of the view itself and pass it into the view as a parameter to initialization.
struct Card {
let kana: String
let romaji: String
}
let myCard = Card(
kana: "Hello",
romaji: "World"
)
struct FlashcardView: View {
let card: Card
@State var isFlipped = false
var body: some View {
return Text(isFlipped ? card.romaji : card.kana)
.onTapGesture {
self.isFlipped.toggle()
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
return FlashcardView(card: myCard)
}
}
#endif
However, if you want the view to change when card
itself changes (not that you necessarily should do that as a logical next step), then this code is insufficient. You'll need to import Combine
and reconfigure the card
variable and the Card
type itself, in addition to figuring out how and where the mutation going to happen. And that's a different question.
Long story short: modify @State
variables within gesture blocks. If you want to modify them outside of the view itself, then you need something else besides a @State
annotation. @State
is for local/private use only.
(I'm using Xcode 11 beta 5)