使用 LazyVGrid 时,DetailView 仅调用数组中的第一项
DetailView only calls on first item in array when using LazyVGrid
我有一个可以添加项目的列表,当我点击它们时,它打开了正确的详细视图。我最近将列表换成 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
。有很多 sheet... 当 detailView
设置为 true 时,它们都会尝试呈现。机缘巧合,第一个出现了。
相反,您需要使用 sheet(item:onDismiss:content:)
。 sheet
的这个替代版本是专门为您的目的而制作的——当您有一个 Item
的数组并且想要为特定的 Item
.[=30 呈现一个 sheet 时=]
首先,您需要使 Item
符合 Identifiable
。
/// here!
struct Item: Hashable, Codable, Equatable, Identifiable {
然后,将旧触发器 @State var detailView = false
替换为 @State var selectedDetailItem: Item?
。另外,确保将 sheet
移到 ForEach
之外,这样它就不会重复。现在,sheet 只会在 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)
}
}
}
结果:
我有一个可以添加项目的列表,当我点击它们时,它打开了正确的详细视图。我最近将列表换成 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
。有很多 sheet... 当 detailView
设置为 true 时,它们都会尝试呈现。机缘巧合,第一个出现了。
相反,您需要使用 sheet(item:onDismiss:content:)
。 sheet
的这个替代版本是专门为您的目的而制作的——当您有一个 Item
的数组并且想要为特定的 Item
.[=30 呈现一个 sheet 时=]
首先,您需要使 Item
符合 Identifiable
。
/// here!
struct Item: Hashable, Codable, Equatable, Identifiable {
然后,将旧触发器 @State var detailView = false
替换为 @State var selectedDetailItem: Item?
。另外,确保将 sheet
移到 ForEach
之外,这样它就不会重复。现在,sheet 只会在 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)
}
}
}
结果: