我想做的是创建一个项目网格,其中每行有 1 个项目或 2 个项目,具体取决于是否选择单元格。有一种边缘情况,其中一个单元格可以单独位于一行上,但未被选中,并且它应该只占用与两个网格项共享空间的空间。单元格唯一应占据网格的整个宽度的时间应该是在选择它时。
GridRow
,但是点击似乎不会更新我的状态。LazyVGrid(columns: [GridItem(.adaptive(minimum: 150)), GridItem(.flexible()) ]) {
ForEach(locationService.locationGroups, id: \.self) { group in
if let item = group.first(where: { $0.selected }) {
GridRow {
TrailDetailCardView(location: item.location)
.onTapGesture {
locationService.setSelected(for: item, to: !item.selected)
}
}
} else {
GridRow {
ForEach(group, id: \.self) { rowItem in
TrailDetailCardView(location: rowItem.location)
.onTapGesture {
locationService.setSelected(for: rowItem, to: !rowItem.selected)
}
}
}
}
}
}
@Published var locationGroups: [[LocationToggleable]]
中进行跟踪,但是 LocationToggleable
不是 Observable
,它只包含一个 selected
属性,我将其放入其中以使对象正确地 Hashable
用于其他用例。我认为这个对象或我的用法就是为什么 .onTapGesture{...}
似乎没有更新我的 LazyVGrid
中的状态。static private func getListGroups(filteredLocations: [LocationToggleable]) -> [[LocationToggleable]] {
var groupedKeys: [[LocationToggleable]] = []
var currentRow: [LocationToggleable] = []
for item in filteredLocations.sorted(by: { $0.location.locationName < $1.location.locationName }) {
if item.selected {
if !currentRow.isEmpty {
groupedKeys.append(currentRow)
currentRow = []
}
groupedKeys.append([item])
} else {
if currentRow.count < 2 {
currentRow.append(item)
} else {
groupedKeys.append(currentRow) // Append the current row to groupedKeys
currentRow = [item]// Start a new row with the current item
}
}
}
if !currentRow.isEmpty {
groupedKeys.append(currentRow)
}
return groupedKeys
}
@Published var locationGroups: [[LocationToggleable]]
,然后更新视图。Grid {
ForEach(locationService.locationGroups, id: \.self) { rowItems in
// Check if this row contains a selected item
if rowItems.count == 1 && rowItems[0].selected {
let selectedItem = rowItems[0]
TrailDetailCardView(location: selectedItem.location)
.frame(maxWidth: .infinity) // To make the card take the full width
.matchedGeometryEffect(id: "location-id-\(selectedItem.id)", in: namespace)
.id(selectedItem.id)
.onTapGesture {
locationService.setSelected(for: selectedItem, to: !selectedItem.selected)
}
} else {
GridRow {
ForEach(rowItems, id: \.self) { item in
TrailDetailCardView(location: item.location)
.matchedGeometryEffect(id: "location-id-\(item.id)", in: namespace)
.id(item.id)
.onTapGesture {
locationService.setSelected(for: item, to: !item.selected)
}
if rowItems.count < 2 && !item.selected && locationService.locationGroups.count < 3 {
TrailDetailCardView(location: item.location)
.disabled(true)
.opacity(0.0)
}
}
}
}
}
}
.lineLimit(1)
.minimumScaleFactor(0.75)
.padding()
解决方案是创建两列,并将
.selected
视为 Section
。特别是将其视为 Section.header
而不仅仅是一个截面体。将来,如果我想要额外的部分,我可以简单地定义它,并将每个部分的其他逻辑保留在正文之外。这并不能解决@State
问题,但它确实允许延迟加载,从而显着提高性能并满足上述要求。
struct LocationListView: View {
@Namespace var namespace: Namespace.ID
@ObservedObject var locationService: LocationService
let gridItems = [
GridItem(.flexible(minimum: UIScreen.main.bounds.width / 2 - 50)),
GridItem(.flexible(minimum: UIScreen.main.bounds.width / 2 - 50))
]
@State var showingFirst = false
var body: some View {
ScrollViewReader { proxy in
ScrollView {
VStack {
LazyVGrid(columns: gridItems) {
ForEach(locationService.toggleableLocations, id: \.self) { item in
if item.selected {
Section(content: {}, header: {
TrailDetailCardView(location: item.location)
.frame(maxWidth: .infinity)
.matchedGeometryEffect(id: "location-id-\(item.id)", in: namespace)
.id(item.id)
.onTapGesture {
proxy.scrollTo(item.id)
locationService.setSelected(for: item, to: !item.selected)
}
})
} else {
TrailDetailCardView(location: item.location)
.matchedGeometryEffect(id: "location-id-\(item.id)", in: namespace)
.id(item.id)
.onTapGesture {
proxy.scrollTo(item.id)
locationService.setSelected(for: item, to: !item.selected)
}
}
}
}
}
.lineLimit(1)
.minimumScaleFactor(0.75)
.padding()
}
}
}
}