SwiftUI实现含图片与名称的单网格视图及导航栏标题传值
解决方案
方案一:统一数据结构(推荐)
先调整分类数据结构,让每个分类项同时包含名称和图片,确保两者一一对应:
struct ContentView: View { struct ProductTypes: Identifiable { let id: Int let name: String // 分类名称,与prodType的key一致 let image: String // 对应图片名称 } // 初始化分类数据,保证name与prodType的key匹配 let typeItems = [ ProductTypes(id: 1, name: "Bakery", image: "bakery"), ProductTypes(id: 2, name: "Dairy", image: "diary"), ProductTypes(id: 3, name: "Fruits", image: "fruits"), ProductTypes(id: 4, name: "Vegans", image: "vegans"), ProductTypes(id: 5, name: "Vegetables", image: "vegetables") ] @ObservedObject var groceryViewModel: ProductViewModel let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 3) var body: some View { NavigationView { ScrollView { LazyVGrid(columns: columns, spacing: 12) { ForEach(typeItems) { item in NavigationLink( // 传递分类名称到下一页 destination: ProductListView(groceryViewModel: groceryViewModel, categoryName: item.name), label: { // 单个网格项:图片在上,名称在下的垂直布局 VStack(spacing: 8) { Image(item.image) .resizable() .clipShape(Circle()) .scaledToFill() .frame(width: 100, height: 100) .clipped() // 防止图片溢出圆形边界 Text(item.name) .font(.system(.headline, weight: .bold)) .foregroundColor(.black) .multilineTextAlignment(.center) } .padding(.vertical, 8) } ) } } .padding() .onAppear { groceryViewModel.readJSONFile(filename: "Groceries") } } .navigationBarTitle("Categories", displayMode: .inline) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView(groceryViewModel: ProductViewModel()) } }
方案二:通过名称匹配图片(无需修改原有数据结构)
如果不想调整ProductTypes结构,可直接遍历prodType的key,通过名称匹配对应图片(假设图片名称为分类名称的小写形式):
struct ContentView: View { struct ProductTypes: Identifiable { let id: Int let image: String } let typeImages = [ProductTypes(id: 1, image: "bakery"),ProductTypes(id: 2, image: "diary"),ProductTypes(id: 3, image: "fruits"),ProductTypes(id: 4, image: "vegans"),ProductTypes(id: 5, image: "vegetables")] @ObservedObject var groceryViewModel : ProductViewModel let columns : [GridItem] = Array(repeating: .init(.flexible()),count: 3) var body: some View { NavigationView { ScrollView{ LazyVGrid(columns: columns,spacing: 12) { ForEach(groceryViewModel.prodType.keys.sorted(),id: \.self) { categoryName in // 根据分类名称匹配对应图片 let imageName = categoryName.lowercased() NavigationLink( destination: ProductListView(groceryViewModel: groceryViewModel, categoryName: categoryName), label:{ VStack(spacing: 8) { Image(imageName) .resizable() .clipShape(Circle()) .scaledToFill() .frame(width: 100,height: 100) .clipped() Text(categoryName) .font(.system(.headline,weight: .bold)) .foregroundColor(.black) .multilineTextAlignment(.center) } .padding(.vertical, 8) } ) } } .padding() .onAppear { groceryViewModel.readJSONFile(filename: "Groceries") } } .navigationBarTitle("Categories",displayMode: .inline) } } } struct ContentView_Previews : PreviewProvider { static var previews: some View{ ContentView( groceryViewModel: ProductViewModel()) } }
下一页(ProductListView)适配修改
无论采用哪种方案,都需要在ProductListView中接收分类名称并设置为导航标题:
struct ProductListView: View { @ObservedObject var groceryViewModel: ProductViewModel let categoryName: String // 接收传递的分类名称 var body: some View { // 此处替换为你的产品列表内容 Text("Products in \(categoryName)") .navigationBarTitle(categoryName, displayMode: .inline) // 设置导航栏标题为分类名称 } }
核心修改说明
- 合并两个独立的
ForEach循环,让每个网格项同时包含图片和对应分类名称 - 通过
NavigationLink的destination参数将分类名称传递到下一页 - 在目标页面直接使用接收的名称设置导航栏标题
内容的提问来源于stack exchange,提问作者Vinduja Narayanan K
相关产品推荐
相关产品推荐

