I'm currently going through the Standford iOS development lectures (linked roughly where I'm at, with mostly relevant code), while slightly straying slightly from what he's doing to learn a bit independently. Still most of the code there is relevant to my question.
Right now I'm trying to get the cards to animate in by showing/hiding them depending on if their ID is in a Set, dealt. In the .onAppear for the content View, I iterate over the cards, and add each of them to the dealt Set. Then, I only display them if isUndealt(card)
import SwiftUI
struct EmojiMemoryGameView: View {
@ObservedObject var game: EmojiMemoryGame
var body: some View {
NavigationView {
content
.navigationTitle(game.themeName)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Text("Score: \(game.score)")
.font(.title3)
.fontWeight(.semibold)
Spacer()
shuffleButton
}
}
}
.navigationViewStyle(.stack)
}
@State private var dealt = Set<String>()
private func deal(_ card: EmojiMemoryGame.Card) {
print("Before insert: \(dealt)")
print("Inserting \(card.id)")
dealt.insert(card.id)
let bah = dealt
print("After insert: \(dealt)")
}
private func isUndealt(_ card: EmojiMemoryGame.Card) -> Bool {
print("dealt: \(dealt)")
let bah = dealt
return !dealt.contains(card.id)
}
var content: some View {
VStack {
themeSelector.frame(maxWidth: .infinity)
AspectVGrid(items: game.cards, aspectRatio: 2/3) { card in
if isUndealt(card) || (card.isMatched && !card.isFaceUp) {
Color.clear
} else {
CardView(card: card)
.padding(4)
.transition(AnyTransition.asymmetric(insertion: .scale, removal: .opacity))
.onTapGesture {
withAnimation {
game.choose(card)
}
}
}
}
.onAppear {
withAnimation {
for card in game.cards {
deal(card)
}
}
}
.foregroundColor(game.themeColor)
.padding(.horizontal)
}
}
...
You'll notice I added logging to deal() and isUndealt() to show when dealt is modified, and what its current contents are. Here's what the output looks like on a sample run (shortened all the inserts to save space):
Before insert: []
Inserting 4AE58783-5F32-4359-8E88-300D73F1AA89
After insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89"]
Before insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89"]
Inserting B82B98B1-47AC-4836-BE01-05FEFAC083E5
After insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5"]
Before insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5"]
Inserting B7A90B6E-C3BC-4F06-92D8-98B7DCAEFA6A
After insert: ["B7A90B6E-C3BC-4F06-92D8-98B7DCAEFA6A", "4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5"]
Before insert: ["B7A90B6E-C3BC-4F06-92D8-98B7DCAEFA6A", "4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5"]
Inserting 3A921774-3CDD-4A0F-85FB-FC1B4089CA8E
After insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5", "B7A90B6E-C3BC-4F06-92D8-98B7DCAEFA6A", "3A921774-3CDD-4A0F-85FB-FC1B4089CA8E"]
Before insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5", "B7A90B6E-C3BC-4F06-92D8-98B7DCAEFA6A", "3A921774-3CDD-4A0F-85FB-FC1B4089CA8E"]
Inserting 8A9FA6C2-AC77-4C46-9A8D-C21992FD4D75
After insert: ["4AE58783-5F32-4359-8E88-300D73F1AA89", "B82B98B1-47AC-4836-BE01-05FEFAC083E5", "B7A90B6E-C3BC-4F06-92D8-98B7DCAEFA6A", "8A9FA6C2-AC77-4C46-9A8D-C21992FD4D75", "3A921774-3CDD-4A0F-85FB-FC1B4089CA8E"]
...
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
As you can see, as I'm adding to the Set from deal(), it's properly adding the IDs as I would expect. However, later when I check dealt from isDealt(), it shows that it's empty every time.
The odd thing is, if I replace the body with what the instructor uses in the tutorial (getting rid of the NavigationView)
var body: some View {
VStack {
content
shuffleButton
}
.padding()
}
Suddenly the animation works, with the output as such:
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
dealt: []
Before insert: []
Inserting 5012E432-9972-4932-8444-1408FEA3188B
After insert: ["5012E432-9972-4932-8444-1408FEA3188B"]
Before insert: ["5012E432-9972-4932-8444-1408FEA3188B"]
Inserting 29CD9E52-83A1-49CF-A280-7CEE9FDB13D3
After insert: ["5012E432-9972-4932-8444-1408FEA3188B", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
Before insert: ["5012E432-9972-4932-8444-1408FEA3188B", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
Inserting 48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E
After insert: ["48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "5012E432-9972-4932-8444-1408FEA3188B", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
...
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
dealt: ["6D182BF3-DDAF-4487-A83E-4939A7AC452B", "5012E432-9972-4932-8444-1408FEA3188B", "B16870E9-71A8-46B0-AE73-D9E292EEFCB0", "96CD9D45-5EE6-4AD3-948D-0176A85BA11F", "48F40FE1-8E4C-4DED-BE2C-35EED7FBD56E", "B998EB7A-9B09-44B8-A64F-D6DE62B7DCBE", "59637C10-E9F1-4A61-8CA2-A1796CC48523", "E6F26A49-8DC8-4926-95EE-DF9ACAB739FE", "B2BD590E-FB65-4101-A07A-616465A7AAAF", "29CD9E52-83A1-49CF-A280-7CEE9FDB13D3"]
Perhaps my question title isn't even the correct thing to be asking, but I'm very confused as to what's going on here. Why is an outer view container changing how my @State works? What's going wrong in the first version of the code?