【问题标题】:DetailView only calls on first item in array when using LazyVGrid使用 LazyVGrid 时,DetailView 仅调用数组中的第一项
【发布时间】:2021-11-20 17:34:16
【问题描述】:

我有一个可以添加项目的列表,当我单击它们时,它会打开正确的详细信息视图。我最近将列表换成了 LazyVGrid,但是当我单击网格项时,详细视图仅调用数组中的第一项。

这是列表视图:

import SwiftUI

struct ListView: View {

    @EnvironmentObject var listViewModel: ListViewModel
    @State var addItemView = false
    @State var detailView = false
    
    let columns = [GridItem(.flexible()), GridItem(.flexible())]

    var body: some View {
        NavigationView {
            ZStack {
                ZStack {
                    Color(.white)
                }
                VStack {
                    Button(action: {
                        self.addItemView = true
                    }, label: {
                        Text("Add")
                    }).sheet(isPresented: $addItemView, content: {
                        AddItemView()
                    })
                    LazyVGrid(columns: columns, spacing: 20) {  // <-- the only line i comment when using list
//                        List {
                        ForEach(listViewModel.item, id:\.id){ item in
                            Button(action: {
                                self.detailView = true
                            }, label: {
                                ListRowView(item: item)
                            }).sheet(isPresented: $detailView, content: {
                                DetailView(item: item)
                            })
                        }
//                            .onDelete(perform: listViewModel.deleteItem)
//                            .onMove(perform: listViewModel.moveItem)
//                            .listRowBackground(Color.blue)
                    }
//                        .listStyle(PlainListStyle())
                    Spacer()
                }
            }
        }
    }
}

struct ListRowView: View {   // <-- using this as grid item
    @State var item:Item

    var body: some View{
        VStack {
            Text(item.name).foregroundColor(.white)
        }.frame(width: 150, height: 150, alignment: .center)
        .background(Color.blue)
        .cornerRadius(10)
    }
}

添加项目和详细视图:

struct AddItemView: View {
    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var listViewModel: ListViewModel
    @State var id = UUID()
    @State var name = ""

    var body: some View {
        TextField("Enter Name", text: $name)
        Button(action: {
            addItem()
        }, label: {
            Text("Done")
        })
    }

    func addItem() {
        listViewModel.addItem(id: id.uuidString, name: name)
        presentationMode.wrappedValue.dismiss()
    }
}

struct DetailView: View {
    @State var item:Item

    var body: some View {
        Text(item.name)
    }
}

这就是我添加每个项目的方式:

import Foundation

struct Item: Hashable, Codable, Equatable {
    var id:String
    var name: String
}

class ListViewModel: ObservableObject {

    @Published var item: [Item] = [] {
        didSet {
            saveItem()
        }
    }

    let itemsKey: String = "items_key"

    init() {
        getItems()
    }

    func getItems() {
        guard
            let data = UserDefaults.standard.data(forKey: itemsKey),
            let savedItems = try? JSONDecoder().decode([Item].self, from: data)
        else { return }
    
        self.item = savedItems
    }

    func deleteItem(indexSet: IndexSet){
        item.remove(atOffsets: indexSet)
    }

    func moveItem(from: IndexSet, to: Int){
        item.move(fromOffsets: from, toOffset: to)
    }

    func addItem(id: String, name: String){
        let newItem = Item(id: id, name: name)
        item.append(newItem)
        print(newItem)
    }

    func saveItem() {
        if let encodedData = try? JSONEncoder().encode(item) {
            UserDefaults.standard.set(encodedData, forKey: itemsKey)
        }
    }
}

我不确定为什么 LazyVGrid 只调用第一项,任何帮助将不胜感激

【问题讨论】:

    标签: arrays list swiftui detailview lazyvgrid


    【解决方案1】:

    问题出在这里:

    @State var detailView = false /// will only work for 1 sheet, not multiple...
    
    ...
    
    ForEach(listViewModel.item, id:\.id){ item in
        Button(action: {
            self.detailView = true
        }, label: {
            ListRowView(item: item)
        }).sheet(isPresented: $detailView, content: { /// not good to have `sheet` inside ForEach
            DetailView(item: item)
        })
    }
    

    ForEach 的每次迭代中,您都有一个sheet。那是很多工作表......当detailView 设置为true 时,所有工作表都会尝试呈现。偶然地,第一次出现。

    相反,您需要使用sheet(item:onDismiss:content:)sheet 的这个替代版本是专门为您的目的而设计的——当您有一个 Items 数组并希望为该特定 Item 提供一个工作表时。

    首先,您需要使 Item 符合 Identifiable

                                               /// here!
    struct Item: Hashable, Codable, Equatable, Identifiable {
    

    然后,将旧触发器 @State var detailView = false 替换为 @State var selectedDetailItem: Item?。另外,请确保将sheet 移出ForEach,以免重复。现在,工作表只会在 selectedDetailItem 不为零时出现。

    struct ListView: View {
    
        @EnvironmentObject var listViewModel: ListViewModel
        @State var addItemView = false
        @State var selectedDetailItem: Item? /// here!
        
        let columns = [GridItem(.flexible()), GridItem(.flexible())]
    
        var body: some View {
            NavigationView {
                ZStack {
                    ZStack {
                        Color(.white)
                    }
                    VStack {
                        Button(action: {
                            self.addItemView = true
                        }, label: {
                            Text("Add")
                        }).sheet(isPresented: $addItemView, content: {
                            AddItemView()
                        })
                        LazyVGrid(columns: columns, spacing: 20) {
                            ForEach(listViewModel.item, id:\.id){ item in
                                Button(action: {
                                    self.selectedDetailItem = item /// set the selected item
                                }, label: {
                                    ListRowView(item: item)
                                })
                            }
                        }
                        Spacer()
                    }
                }
            } /// present the sheet here
            .sheet(item: $selectedDetailItem) { selectedItem in
                DetailView(item: selectedItem)
            }
        }
    }
    

    结果:

    【讨论】:

      猜你喜欢
      • 2020-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-02
      • 1970-01-01
      相关资源
      最近更新 更多