使用LazyVGrid/LazyVStack时按钮图标切换异常问题求助
问题原因分析
这个问题的核心是LazyVGrid的视图复用机制加上Favorites类的状态变更没有正确通知视图刷新导致的:
- Lazy容器的复用特性:LazyVGrid只会渲染当前可见区域的视图,当你滚动到不可见区域时,之前的视图会被复用给新出现的单元格。如果视图的状态没有正确绑定到可观察的数据源,复用的时候就会显示旧的状态。
- Favorites的状态通知缺失:你的
Favorites类里,用来判断游戏是否收藏的games数组不是@Published属性。虽然你手动调用了objectWillChange.send(),但这个调用时机不够可靠,而且@Published会自动处理状态变更的通知,确保所有依赖的视图都能收到更新。
解决方案
1. 修复Favorites类的状态通知
把games数组改成@Published属性,这样当它的内容变化时,会自动触发ObservableObject的变更通知,所有依赖它的视图都会刷新。同时可以去掉手动的objectWillChange.send()调用,因为@Published会帮我们处理:
class Favorites: ObservableObject { //The fetched games by id are stored here. @Published var favGames: [Game] = [] @Published var isLoading = false // 👇 把games改成@Published @Published var games: [String] var gameService = Store.shared let userDefaults = UserDefaults.standard // the key we're using to read/write in UserDefaults private let saveKey = "Favorites" init() { // load our saved data self.games = userDefaults.stringArray(forKey: saveKey) ?? [] } // returns true if set contains the game func contains(_ game: Game) -> Bool { return games.contains(String(game.id)) } // adds game to set, updates all views, and saves the change func add(_ game: Game) { // 👇 移除手动的objectWillChange.send() games.append(String(game.id)) save() } // removes the game from set, updates all views, and saves the change func remove(_ game: Game) { // 👇 移除手动的objectWillChange.send() games.remove(object: String(game.id)) save() } // ... 其他方法保持不变 }
2. 确保GameCard正确响应状态变更
因为你已经在GameCard里使用了@EnvironmentObject var favorites: Favorites,只要favorites.games是@Published的,当它变化时,GameCard的body会自动重新计算,图标就会正确切换了。
额外优化:避免LazyVGrid复用导致的状态残留
如果上面的修改后还有偶尔的问题,可以给GameCard加上一个明确的id绑定,确保LazyVGrid能正确识别每个单元格的状态:
在GameListView的LazyVGrid里的GameCard添加id:
GameCard(game: game) .id(game.id) // 👇 添加这个,确保每个单元格有唯一标识 .onTapGesture { self.selectedGame = game print(self.selectedGame!) }
这样LazyVGrid会把每个GameCard和对应的游戏ID绑定,复用的时候会优先匹配ID,减少状态残留的可能。
为什么普通VStack没问题?
普通VStack会一次性渲染所有视图,每个视图都是独立的,不会被复用,所以即使状态通知有问题,点击的时候视图也会因为按钮动作触发的局部刷新而更新。但Lazy容器的复用机制放大了状态通知缺失的问题,导致旧状态被复用。
内容的提问来源于stack exchange,提问作者Svenson
相关产品推荐
相关产品推荐

