SwiftUI跨视图传递product_id异常:首次打开Sheet无值
问题分析:首次打开Sheet时product_id传递失效的原因及解决办法
项目中用户可拥有两个收藏商品,点击商品后弹出ProductSheet视图,尝试传递前一个视图的product_id到ProductSheet和ProductRowView以实现修改收藏功能,但首次打开Sheet时product_id无值,二次打开正常。
相关代码
FavouriteProducts视图
struct FavouriteProducts: View { @StateObject var favouriteProductsModel = FavouriteProductsViewModel() @State private var showingSheet = false @State var selectedProduct: String? @State var product_id: String? var body: some View { ZStack { let defaults = UserDefaults.standard let keyString: String? = defaults.string(forKey: "key") ?? "" VStack { HStack { Text("Tap to change favourite product") } HStack(spacing: 25) { ForEach(favouriteProductsModel.favourite_products, id: \.id){ product in VStack { Image("ProductImage") .resizable() Text(String(product.product_name ?? "")) } .onTapGesture { selectedProduct = product.id self.product_id = product.id ?? "" self.showingSheet = true //selectedProduct does have a value in this print statement print("Tapped Product ID: \(String(describing: selectedProduct))") } .sheet(item: $selectedProduct) { favouriteProductsModel.fetchFavouriteProducts(key: keyString ?? "") } content: { item in ProductSheet(product_id: selectedProduct ?? "") } } } } } .onLoad { let defaults = UserDefaults.standard let keyString: String? = defaults.string(forKey: "key") ?? "" self.favouriteProductsModel.fetchFavouriteProducts(key: keyString ?? "") } } }
ProductSheet视图
struct ProductSheet: View { @StateObject var marketplaceModel = MarketplaceViewModel() @State var product_id: String? var body: some View { VStack { // The product_id here is not being displayed, because the variable has no value Text(product_id ?? "") ForEach(marketplaceModel.filteredProduct, id:\.self){ product in ProductRowView(productData: product, product_id: product_id ?? "") } } } }
ProductRowView视图
struct ProductRowView: View { @Environment(\.dismiss) var dismiss @StateObject var favouriteProductsModel = FavouriteProductsViewModel() @State private var isShowingDetailView = false @State var selectedProduct : ProductTile? @State var new_id = "" var productData: ProductRow var product_id: String var body: some View { VStack { HStack { Text(productData.title) HStack { NavigationLink("", isActive: $isShowingDetailView) { ProductSeeAllView(productData: productData, product_id: productData.id) }.navigationTitle("") Button("See All") { isShowingDetailView = true } Image(systemName: "chevron.right") } } HStack { ScrollView { HStack { ForEach(productData.products ?? []) { product in ProductView(productData: product) .onTapGesture { new_id = product.id ?? "" selectedProduct = product print("Product id: \(selectedProduct?.id ?? "")") let defaults = UserDefaults.standard let keyString: String? = defaults.string(forKey: "key") ?? "" favouriteProductsModel.changeFavouriteProducts(key: keyString ?? "", current_id: product_id, new_id: new_id ) { (success) in if success { dismiss() } else { print("Unsuccessful...") } } } } } } } } } }
原因解析
- Sheet初始化时序问题:在
FavouriteProducts的.sheet(item:)闭包中,你使用了selectedProduct而不是闭包提供的item参数。SwiftUI首次触发Sheet时,selectedProduct的状态更新和Sheet初始化存在时序差,导致Sheet拿到的是旧的nil值;第二次打开时selectedProduct已有上次的有效值,所以能正常传递。 - 错误的状态声明:
ProductSheet中将外部传入的product_id声明为@State,@State用于视图内部管理的状态,会忽略外部传入的初始值,默认使用nil,这也是首次打开时无值的核心原因之一。
修复方案
使用Sheet闭包的item参数传递值:
修改FavouriteProducts中.sheet的content闭包,直接使用item(即选中的product.id):.sheet(item: $selectedProduct) { favouriteProductsModel.fetchFavouriteProducts(key: keyString ?? "") } content: { item in ProductSheet(product_id: item) }同时可以删除
FavouriteProducts中多余的product_id状态变量。修正ProductSheet的变量声明:
将ProductSheet中的@State var product_id: String?改为普通变量,因为它是外部传入的常量:struct ProductSheet: View { @StateObject var marketplaceModel = MarketplaceViewModel() var product_id: String // 剩余代码不变 }
这样修改后,SwiftUI会确保Sheet初始化时拿到最新的选中product_id值,同时避免@State覆盖传入值的问题,首次打开Sheet时就能正常传递product_id。
内容的提问来源于stack exchange,提问作者Mxyb
相关产品推荐
相关产品推荐

