Swift UI 视图仅显示一条记录而不是记录列表

问题描述 投票:0回答:1

我正在使用 Swift UI。我使用 async 和 wait 来处理并发。我正在尝试显示产品记录列表。我有 30 条记录为 json,这里是 json 链接https://dummyjson.com/products。当我调试它时,我得到了 30 条记录,但是当我运行应用程序时,它只显示一条记录。我想知道哪里有错误或需要更改。

这是我的模型..

import Foundation

// MARK: - Welcome
struct ProductData: Codable, Hashable {
    let products: [Product]
}

// MARK: - Product
struct Product: Codable, Hashable {
    let id: Int
    let title, description: String
    let price: Int
    let discountPercentage, rating: Double
    let stock: Int
    let brand, category: String
    let thumbnail: String
    let images: [String]
}

这是我的视图模型代码..

@MainActor
final class ProductListViewModel {

    @Published private(set) var productLists: [ProductData] = []
    @Published private(set) var customError: NetworkError?
    @Published private(set) var refreshing = true
    @Published var isErrorOccured = false

    private let repository: ProductRepository
    init(repository: ProductRepository) {
        self.repository = repository
    }
}
extension ProductListViewModel: ProductListViewModelAction {
    func getProductList(urlStr: String) async {
            refreshing = true
            guard let url = URL(string: urlStr) else {
            self.customError = NetworkError.invalidURL
            refreshing = false
            isErrorOccured = false
            return
        }
        do {
            let lists = try await repository.getList(for: url)
            refreshing = false
            isErrorOccured = false
            productLists = [lists]

        } catch {
            refreshing = false
            isErrorOccured = true
            customError = error as? NetworkError
        }
    }
}

这是我的观点..

import SwiftUI

struct ProductListView: View {
    
    @StateObject var viewModel = ProductListViewModel(repository: ProductRepositoryImplementation(networkManager: NetworkManager()))

    var body: some View {
        NavigationStack {
            VStack {
                if viewModel.customError != nil && !viewModel.refreshing {
                    alertView()
                } else {
                    if viewModel.refreshing {
                        progressView()
                    }
                    if viewModel.productLists.count > 0 && !viewModel.refreshing {

                        List(viewModel.productLists, id: \.self) { product in
                            ProductListViewCell(productData: product)

                        } .listStyle(.grouped)
                    }
                }
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    getToolBarView()
                }
            }
            .navigationTitle(Text("Product List"))
        }.task{
            await getDataFromAPI()
        }
        .refreshable {
            await getDataFromAPI()
        }
    }

    func getDataFromAPI() async {
        await viewModel.getProductList(urlStr: NetworkURL.productUrl)
    }

    @ViewBuilder
    func getToolBarView() -> some View {
        Button {
            Task{
                await getDataFromAPI()
            }
        } label: {
            HStack {
                Image(systemName: "arrow.clockwise")
                    .padding(.all, 10.0)
            }.fixedSize()
        }
        .cornerRadius(5.0)
    }

    @ViewBuilder
    func progressView() -> some View {
        VStack{
            RoundedRectangle(cornerRadius: 15)
                .fill(.white)
                .frame(height: 180)
                .overlay {
                    VStack {
                        ProgressView().padding(50)
                        Text("Please Wait Message").font(.headline)
                    }
                }
        }
    }

    @ViewBuilder
    func alertView() -> some View {
        Text("").alert(isPresented: $viewModel.isErrorOccured) {
            Alert(title: Text("General_Error"), message: Text(viewModel.customError?.localizedDescription ?? ""),dismissButton: .default(Text("Okay")))
        }
    }
}

这是我的单元格视图..

import SwiftUI

struct ProductListViewCell: View {
    let productData: ProductData
    var body: some View {
        HStack {
            if let url = URL(string: productData.products.first?.thumbnail ?? ""){
                ProductAsyncImageView(url: url)
                    .frame(width: 150, height: 150)
                    .mask(RoundedRectangle(cornerRadius: 16))
            }
            VStack(alignment: .leading,spacing: 5){
                Text("Product Name: " +  (productData.products.first?.title ?? ""))
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.headline)

                Text("Product Description: " + (productData.products.first?.description ?? ""))
                    .frame(maxWidth: .infinity, alignment: .leading)


                Text("Product Price: " + String(productData.products.first?.price ?? 0))
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.subheadline)
            }
        }
    }
}

这是调试结果..

这是截图..只显示一条记录。

arrays swift swiftui async-await viewmodel
1个回答
0
投票

productLists = [lists]
创建一个包含一项的数组。现在,该
ProductData
中可能有 30 个产品,但
ProductListViewCell
随后仅为该
ProductData
中的第一个产品创建一个单元格。因此,只会显示一项。

我建议产品列表视图模型应该只采用一系列产品:

final class ProductListViewModel: ObservableObject {
    @Published private(set) var products: [Product] = []    // an array of products

    …
}

extension ProductListViewModel {
    func getProductList(urlStr: String) async {
        refreshing = true
        guard let url = URL(string: urlStr) else { … }

        do {
            let productList = try await repository.getList(for: url)
            refreshing = false
            isErrorOccured = false
            products = productList.products                 // get the list of products
        } catch {
            …
        }
    }
}

然后

ProductListView
将显示这个
Product
数组:

struct ProductListView: View {
    @StateObject var viewModel = …
    
    var body: some View {
        NavigationStack {
            VStack {
                …
                if viewModel.products.count > 0 && !viewModel.refreshing {
                    List(viewModel.products, id: \.self) { product in
                        ProductListViewCell(product: product)
                    } .listStyle(.grouped)
                }
            }
} 

然后单元格只需要一个

Product
:

struct ProductListViewCell: View {
    let product: Product
    
    var body: some View {
        HStack {
            if let url = URL(string: product.thumbnail) { … }

            VStack(alignment: .leading, spacing: 5) {
                Text("Product Name: \(product.title)")
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.headline)
                
                Text("Product Description: \(product.description)")
                    .frame(maxWidth: .infinity, alignment: .leading)
                
                Text("Product Price: \(product.price)")
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.subheadline)
            }
        }
    }
}

现在,给猫剥皮的方法有很多种:如果你愿意,你可以使用你的

ProductData
。但关键的带回家的信息
ProductListViewCell
是针对每个
Product
的,因此给它一个完整的
ProductData
并仅访问
first
中包含的 30 种产品阵列中的
ProductData
产品是没有意义的。 .

© www.soinside.com 2019 - 2024. All rights reserved.