如何在SwiftUI中通过多个视图传递子视图的值?

huangapple go评论72阅读模式
英文:

How do I pass a value from a Child view across multiple views in SwiftUI?

问题

在我项目中,当我点击按钮时,我需要将“Product”的id打印到控制台。我可以在一个子视图中访问产品的“id”并将其打印到控制台(在子视图内部被点击时)。我需要在不同视图中的按钮中使用这个id。我该如何做到这一点?这是视图的结构:

struct IntroView: View {
    var body: some View {
        VStack {
            CustomSelectView()
            Button("Continue") {
                // 这里是需要访问ProductRowViewOnboard()中的“selectedProduct”数据的地方
            }
        }
    }
}

struct CustomSelectView: View {
    @State private var showingProductSheet = false
    var body: some View {
        HStack {
            VStack {
                Text("Select Product 1")
            }
            .onTapGesture {
                print("Select Product 1 Tapped...")
                showingProductSheet.toggle()
            }
            .sheet(isPresented: $showingProductSheet) {
                ProductSheetOnboard()
            }
            VStack {
                Text("Select Product 2")
            }
            .onTapGesture {
                print("Select Product 2 Tapped...")
                showingProductSheet.toggle()
            }
            .sheet(isPresented: $showingProductSheet) {
                ProductSheetOnboard()
            }
        }
    }
}

struct ProductSheetOnboard: View {
    @Environment(\.dismiss) var dismiss
    @StateObject var marketplaceModel = MarketplaceViewModel()
    let product_id: String
    var body: some View {
        var columns = Array(repeating: GridItem(.flexible()), count: 2)
        let defaults = UserDefaults.standard
        let keyString = defaults.string(forKey: "key") ?? ""
        ScrollView(.vertical, showsIndicators: false) {
            VStack {
                    ForEach(marketplaceModel.filteredProduct, id:\.self){ product in
                        ProductRowViewOnboard(productData: product,  product_id: product_id ?? "")
                    }
            }
        }
        .onAppear {
            let defaults = UserDefaults.standard
            let keyString = defaults.string(forKey: "key") ?? ""
            self.marketplaceModel.fetchProductTags(key: keyString ?? "")
            self.marketplaceModel.fetchMarketProducts(key: keyString ?? "")
        }
    }
}

struct ProductRowViewOnboard: View {
    @Environment(\.dismiss) var dismiss
    @State var selectedProduct : ProductTile?
    var productData: ProductRow
    let product_id: String
    var body: some View {
        HStack {
            ScrollView {
                HStack {
                    ForEach(productData.products ?? []) { product in
                        ProductView(productData: product)
                            .onTapGesture {
                                selectedProduct = product
// 下面的print语句有效
                                print("Product id: \(selectedProduct?.id ?? "")")
                                let defaults = UserDefaults.standard
                                let keyString: String? = defaults.string(forKey: "key") ?? ""
                                // 这里需要访问selectedProduct中的数据以在IntroView()中使用
                            }
                    }
                }
            }
        }
    }
}
英文:

In my project, I need an id of a 'Product' to be printed to the console when I tap a button. I am accessing an 'id' of a product in one of my child views and printing it to the console (when it is tapped within a child view). I need to use this id in a button in a different view. How do I do this? Here is the structure of the views:

struct IntroView: View {
var body: some View {
VStack {
CustomSelectView()
Button("Continue") {
//Here is where the 'selectedProduct' data in ProductRowViewOnboard() needs to be accessed
}
}
}
}
struct CustomSelectView: View {
@State private var showingProductSheet = false
var body: some View {
HStack {
VStack {
Text("Select Product 1")
}
.onTapGesture {
print("Select Product 1 Tapped...")
showingProductSheet.toggle()
}
.sheet(isPresented: $showingProductSheet) {
ProductSheetOnboard()
}
VStack {
Text("Select Product 2")
}
.onTapGesture {
print("Select Product 2 Tapped...")
showingProductSheet.toggle()
}
.sheet(isPresented: $showingProductSheet) {
ProductSheetOnboard()
}
}
}
}
struct ProductSheetOnboard: View {
@Environment(\.dismiss) var dismiss
@StateObject var marketplaceModel = MarketplaceViewModel()
let product_id: String
var body: some View {
var columns = Array(repeating: GridItem(.flexible()), count: 2)
let defaults = UserDefaults.standard
let keyString = defaults.string(forKey: "key") ?? ""
ScrollView(.vertical, showsIndicators: false) {
VStack {
ForEach(marketplaceModel.filteredProduct, id:\.self){ product in
ProductRowViewOnboard(productData: product,  product_id: product_id ?? "")
}
}
}
.onAppear {
let defaults = UserDefaults.standard
let keyString = defaults.string(forKey: "key") ?? ""
self.marketplaceModel.fetchProductTags(key: keyString ?? "")
self.marketplaceModel.fetchMarketProducts(key: keyString ?? "")
}
}
}
struct ProductRowViewOnboard: View {
@Environment(\.dismiss) var dismiss
@State var selectedProduct : ProductTile?
var productData: ProductRow
let product_id: String
var body: some View {
HStack {
ScrollView {
HStack {
ForEach(productData.products ?? []) { product in
ProductView(productData: product)
.onTapGesture {
selectedProduct = product
//The following print statement works 
print("Product id: \(selectedProduct?.id ?? "")")
let defaults = UserDefaults.standard
let keyString: String? = defaults.string(forKey: "key") ?? ""
// The data in selectedProduct here needs to be accessed in IntroView()
}
}
}
}
}
}
}

In the last view I provided (ProductRowViewOnboard) in the .onTapGesture{}, I have accused the id of the product. I need to access this value in IntroView() so I can use it in the Button.

I can't seem to do it.

答案1

得分: 1

你需要先将状态提升到需要的位置,然后传递let以进行读取访问,或者传递@Binding var以进行读/写访问,例如:

struct IntroView: View {
    @State var selectedProduct : ProductTile?

    var body: some View {
        ... 
        ProductRowViewOnboard(selectedProduct: $selectedProduct)
        ...
    }
}

struct ProductRowViewOnboard: View {
    @Binding var selectedProduct : ProductTile?
}

此外,在SwiftUI中,View结构体应该是您封装视图数据的主要方式。不要使用视图模型对象,因为您将在其中难以传递其他视图数据(正如您可能通过非标准使用onAppear所经历的);您还将遇到SwiftUI通过值类型设计为View结构体以消除的一致性错误。

英文:

You need to move the state up to where it is needed first and then pass down let for read access or @Binding var for read/write access, e.g.

struct IntroView: View {
@State var selectedProduct : ProductTile?
var body: some View {
... 
ProductRowViewOnboard(selectedProduct: $selectedProduct)
...
}
}
struct ProductRowViewOnboard: View {
@Binding var selectedProduct : ProductTile?
}

Also in SwiftUI, the View struct should be your primary encapsulation of view data. Don't use view model objects because you'll have trouble getting other view data in and out of them (as you have probably experienced by your non-standard use of onAppear); you'll also get consistency bugs which SwiftUI's use of value types for the View struct is designed to eliminate.

huangapple
  • 本文由 发表于 2023年5月22日 05:14:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76301948.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定