SwiftUI点击New Game按钮后卡片视图未更新问题解决
问题根因
点击New Game按钮不生效是4个代码逻辑问题共同导致的:
ContentView中声明的MemoryGameTheme实例没有加状态包装器,SwiftUI不会跟踪该属性的变化,且按钮回调里调用的onActivate()方法不存在,点击后没有触发游戏模型重建。MemoryGameTheme中所有主题相关属性都被定义为static静态属性,修改静态属性不会触发ObservableObject的更新通知,视图无法感知主题变化。- 点击按钮后仅调用了主题刷新方法,没有通知
EmojiMemoryGame用新主题重新生成卡片数组,旧的卡片数据一直被@Published持有,自然不会更新。 - 主题名称显示直接读取类的静态属性,不会跟着当前选中的主题动态变化,卡片颜色也写死为红色,没有适配主题配置。
修复方案
MemoryGame结构体的配对、翻牌逻辑本身没有问题,只需要修改主题类、游戏视图模型、主视图三个文件的逻辑即可。
1. 重构MemoryGameTheme类
删除静态属性,改用@Published标记的实例属性,保证属性更新时能自动触发视图刷新:
import SwiftUI class MemoryGameTheme: ObservableObject { enum ChoiseTheme: CaseIterable { case car, animal, item, food, face } @Published var choiseTheme: [String] @Published var themeName: String @Published var colorCard: Color init() { // 初始默认加载动物主题 self.choiseTheme = ["🐣", "🙉", "🙊", "🙈", "🐵", "🐽", "🐸", "🦊", "🐨", "🐹", "🐰", "🐻", "🐼", "🐻❄️", "🐶", "🐱", "🐮"] self.themeName = "Animals" self.colorCard = .gray // 初始化时随机选择主题 refreshTheme() } private func theme(for selectedTheme: ChoiseTheme) { switch selectedTheme { case .car: choiseTheme = ["🚗", "🚕", "🚙", "🚌", "🚎", "🏎", "🚓", "🚑", "🚒", "🚐", "🛻", "🚚", "🚛", "🚜", "🏍", "🚲", "🚁", "✈️", "🛵", "🚀", "🛶", "🛸", "⛵️", "🚅"] themeName = "Cars" colorCard = .red case .animal: choiseTheme = ["🐣", "🙉", "🙊", "🙈", "🐵", "🐽", "🐸", "🦊", "🐨", "🐹", "🐰", "🐻", "🐼", "🐻❄️", "🐶", "🐱", "🐮"] themeName = "Animals" colorCard = .gray case .item: choiseTheme = ["💻", "⌚️", "📱", "⌨️", "🖥", "🖨", "🖲", "🕹", "🎥", "📷", "☎️", "📺", "⏰", "🎛", "📟", "📡", "🧯"] themeName = "Items" colorCard = .purple case .food: choiseTheme = ["🍎", "🍏", "🍊", "🍐", "🍋", "🍌", "🥭", "🍒", "🍆", "🍑", "🍓", "🫐", "🫒", "🧀", "🍟", "🍕", "🥙"] themeName = "Food" colorCard = .yellow case .face: choiseTheme = ["😀", "🥳", "😋", "😗", "🥰", "😍", "😛", "😌", "😡", "🤯", "🤬", "🥵", "😫", "🧐", "☺️", "😃", "😄", "😁"] themeName = "Face" colorCard = .green } } func refreshTheme() { let randomTheme = ChoiseTheme.allCases.randomElement()! theme(for: randomTheme) } }
2. 给EmojiMemoryGame增加新游戏重置逻辑
让视图模型持有主题实例,新增newGame()方法统一处理主题刷新、卡片重建逻辑,@Published标记的模型更新后会自动触发视图刷新:
import SwiftUI class EmojiMemoryGame: ObservableObject { private var theme: MemoryGameTheme init(theme: MemoryGameTheme) { self.theme = theme self.model = EmojiMemoryGame.createMemoryGame(with: theme) } static func createMemoryGame(with theme: MemoryGameTheme) -> MemoryGame<String> { return MemoryGame<String>(numberOfPairOfCards: theme.choiseTheme.count) { pairIndex in theme.choiseTheme[pairIndex] } } @Published private var model: MemoryGame<String> var cards: Array<MemoryGame<String>.Card> { model.cards } // 新游戏重置逻辑 func newGame() { theme.refreshTheme() model = EmojiMemoryGame.createMemoryGame(with: theme) } func choose(_ card: MemoryGame<String>.Card) { model.choose(card) } // 暴露主题属性给视图使用 var currentThemeName: String { theme.themeName } var currentThemeColor: Color { theme.colorCard } }
3. 修改ContentView绑定逻辑
用状态包装器正确持有可观察对象,删除不存在的方法调用,卡片颜色适配当前主题:
struct ContentView: View { @StateObject private var viewModel: EmojiMemoryGame @StateObject private var themeModel: MemoryGameTheme init() { let theme = MemoryGameTheme() _themeModel = StateObject(wrappedValue: theme) _viewModel = StateObject(wrappedValue: EmojiMemoryGame(theme: theme)) } var body: some View { VStack { ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 75))]) { ForEach (viewModel.cards) { card in CardView(card: card, cardColor: viewModel.currentThemeColor) .aspectRatio(2/3, contentMode: .fit) .onTapGesture { viewModel.choose(card) } } } } .foregroundColor(viewModel.currentThemeColor) Spacer() HStack { newGame Spacer() nameTheme } } .padding(.horizontal) } var newGame: some View { Button(action: { viewModel.newGame() }, label: { Text("New Game") .font(.title) .fontWeight(.heavy) }) } var nameTheme: some View { Text(viewModel.currentThemeName) .font(.title3) .fontWeight(.heavy) } } struct CardView: View { let card: MemoryGame<String>.Card let cardColor: Color var body: some View { ZStack { let shape = RoundedRectangle(cornerRadius: 20) if card.isFaceUp { shape.fill().foregroundColor(.white) shape.strokeBorder(lineWidth: 3) Text(card.content).font(.largeTitle) } else if card.isMatched { shape.opacity(0) } else { shape.fill().foregroundColor(cardColor) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .preferredColorScheme(.dark) ContentView() .preferredColorScheme(.light) } }
修改完成后点击New Game按钮,会随机切换主题、重新洗牌生成新的卡片组,主题名称和卡片颜色也会同步更新。
内容的提问来源于stack exchange,提问作者Sem
相关产品推荐
相关产品推荐

