You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中点击心形按钮实现商品收藏/取消收藏及列表展示问题

收藏视图无法展示数据的问题修复方案

核心问题分析

  1. ViewModel数据不一致:

    • 你在FavouriteViewModel里维护了favourites和favItems两个数组,业务操作(添加/删除)基于favourites,但收藏视图展示的是favItems,两者未正确同步。
    • favItems未用@Published修饰,数据更新时无法触发UI刷新。
    • addToFav方法同时调用insert和append,导致同一商品被重复添加,数据冗余。
  2. 视图层ViewModel实例不共享:

    • ProductListView和FavouriteView各自初始化独立的FavouriteViewModel实例,数据完全隔离,列表页的操作无法同步到收藏视图。

具体修复步骤

1. 重构FavouriteViewModel

简化数据源,确保数据变更能触发UI更新:

import Foundation
class FavouriteViewModel : ObservableObject {
    // 统一用该数组维护收藏数据,@Published确保变更通知视图
    @Published var favourites = [ProductData]()
    let defaults = UserDefaults.standard
    
    init() {
        // 初始化时从UserDefaults加载数据到favourites
        guard let data = defaults.data(forKey: "Favourites") else { return }
        self.favourites = (try? PropertyListDecoder().decode([ProductData].self, from: data)) ?? []
    }
    
    func contains(productItem: ProductData) -> Bool {
        favourites.contains(productItem)
    }
    
    func addToFav(productItem: ProductData) {
        guard !contains(productItem: productItem) else { return } // 避免重复添加
        favourites.append(productItem)
        save()
    }
    
    func removeFromFav(productItem: ProductData) {
        if let index = favourites.lastIndex(of: productItem) {
            favourites.remove(at: index)
        }
        save()
    }
    
    func save() {
        defaults.set(try? PropertyListEncoder().encode(favourites), forKey: "Favourites")
    }
}
  • 移除冗余的favItems和getDataFromUserDefaults方法,直接用favourites作为唯一数据源。
  • 修复addToFav的重复添加问题,添加前先判断是否已存在。
  • 所有操作直接修改@Published修饰的favourites,确保UI能感知数据变化。

2. 共享ViewModel实例

使用环境对象(EnvironmentObject)确保两个视图共用同一个ViewModel:

步骤1:在父视图注入ViewModel

假设根视图为ContentView,初始化ViewModel并注入环境:

struct ContentView: View {
    @StateObject private var favVM = FavouriteViewModel()
    
    var body: some View {
        TabView {
            ProductListView(item: "商品列表", filteredData: [/*你的商品数据*/])
                .tabItem { Label("商品", systemImage: "list.bullet") }
            FavouriteView()
                .tabItem { Label("收藏", systemImage: "heart.fill") }
        }
        .environmentObject(favVM) // 注入环境对象
    }
}

步骤2:在子视图中获取共享的ViewModel

修改ProductListView:

struct ProductListView: View {
    @EnvironmentObject var favouriteViewModel: FavouriteViewModel // 从环境获取
    @State var item : String
    @State var filteredData : [ProductData]
    
    // 其余代码保持不变,按钮逻辑无需修改
}

修改FavouriteView:

struct FavouriteView: View {
    @EnvironmentObject var favouriteViewModel: FavouriteViewModel // 从环境获取
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(favouriteViewModel.favourites) { item in // 直接使用favourites数组
                    ZStack {
                        RoundedRectangle(cornerRadius: 20,style: .circular)
                            .fill(.white)
                            .shadow(radius: 1)
                            .frame(width: 360,height: 120)
                        
                        HStack(spacing:10){
                            KFImage(URL(string: item.filename))
                                .resizable()
                                .frame(width: 50,height: 50)
                                .scaledToFit()
                                .padding(.leading,20)
                            
                            VStack(alignment: .leading,spacing: 10) {
                                Text(item.title)
                                Text("\(item.price,specifier: "%.2f")/1Kg")
                                    .foregroundColor(Color.gray)
                            }
                            Spacer()
                            Button(action: {
                                // 收藏视图支持取消收藏
                                favouriteViewModel.removeFromFav(productItem: item)
                            }) {
                                Image("heartfill")
                                    .padding(.trailing,10)
                            }
                        }
                    }
                }
            }
            .frame(maxWidth: .infinity)
            .edgesIgnoringSafeArea(.horizontal)
            .listStyle(GroupedListStyle())
            .navigationBarTitle(Text("Favourites"),displayMode: .inline)
        }
    }
}
  • 移除onAppear里的getDataFromUserDefaults调用,ViewModel初始化时已加载数据,且数据变更会自动通知视图。
  • 优化收藏视图的按钮逻辑,支持直接取消收藏。

3. 优化Hashable实现(可选)

如果希望仅用id判断商品是否为同一收藏项,可自定义Hashable实现:

struct ProductData : Identifiable,Codable,Hashable {
    var id : Int
    var title :String
    var type : String
    var description : String
    var filename : String
    var price : Float
    var rating : Int
    
    // 自定义相等判断,仅比较id
    static func == (lhs: ProductData, rhs: ProductData) -> Bool {
        lhs.id == rhs.id
    }
    
    // 自定义哈希值,仅基于id
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    // 其余代码不变
}

修复后效果

  • 列表页点击心形按钮,收藏数据同步保存到UserDefaults,按钮样式正确切换。
  • 收藏视图自动刷新,展示最新收藏列表,支持点击取消收藏。
  • 两个视图共享同一数据源,数据完全同步。

内容的提问来源于stack exchange,提问作者Vinduja Narayanan K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:17:25