arrays - 使用 LazyVGrid 时,DetailView 仅调用数组中的第一项
问题描述
我有一个可以添加项目的列表,当我单击它们时,它会打开正确的详细信息视图。我最近将列表换成了 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 只调用第一项,任何帮助将不胜感激
解决方案
问题在这里:
@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
是专门为您的目的而制作的——当您有一个Item
s 数组并想要为该特定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)
}
}
}
结果:
推荐阅读
- mysql - MySQL:计算 ETF 投资组合的加权持股量,得到一个包含所有持股量的列表
- html - 如何使用 CSS 动画将元素从左向右移动?
- java - 错误膨胀类 androidx.appcompat.widget.Toolbar android
- javascript - 使用 laravel Inertia JS Vuetify DataTable
- elasticsearch - 如何在弹性搜索中分组
- javascript - 单击删除按钮时如何删除该行?
- spring - Apache poi 4.1.2 上的 SXSSFSheet autoSizeColumn 错误
- matlab - matlab - 对象分割
- .net-core - 如何在dotnet core中使用godaddy office365发送电子邮件?
- kotlin - Gradle 注释处理器顺序 (Android)