我有一个带有列表的视图,根据我单击的列表项,我需要打开一个不同的 NavigationLink。
我有一个用于列表的一行的行模型(我说的是列表,即使我实际上通常使用一个名为 Collection 的定制元素,它是一个带有 HStack 和 VStack 的列表来模拟UICollectionView)
在行模型视图中,我只能为 NavigationLink 指定一个目的地。
我能想到的一个解决方案是获取被点击的列表项的索引并基于该索引打开某个视图。但我无法让它工作。
我基本上需要 List 中的每个元素打开一个不同的视图。
任何帮助表示赞赏! 谢谢 !:)
GroupDetail.swift
import SwiftUI
// data displayed in the collection view
var itemsGroupData = [
ItemsGroupModel("Projects"), // should open ProjectsView()
ItemsGroupModel("People"), //should open PeopleView()
ItemsGroupModel("Agenda"), //should open AgendaView()
ItemsGroupModel("Name") //should open NameView()
]
// main view where the collection view is shown
struct GroupDetail: View {
var body: some View {
// FAMOUS COLLECTION ELEMENT
Collection(itemsGroupData, columns: 2, scrollIndicators: false) { index in
ItemsGroupRow(data: index)
}
}
}
// model of the data
struct ItemsGroupModel: Identifiable {
var id: UUID
let title: String
init(_ title: String) {
self.id = UUID()
self.title = title
}
}
// row type of the collection view
struct ItemsGroupRow: View {
var body: some View {
// This model is for one row
// I need to specify what view to open depending on what item of the collection view was selected (clicked on)
NavigationLink(destination: ProjectsView()) { // open ProjectsView only if the user clicked on the item "Projects" of the list etc..
Text(data.title)
}
}
}
-----------------------------------------------------------------------
Collection.swift
// this custom struct creates the UIKit equivalent of the UICollectionView
// it uses a HStack and a VStack to make columns and rows
import SwiftUI
@available(iOS 13.0, OSX 10.15, *)
public struct Collection<Data, Content>: View
where Data: RandomAccessCollection, Content: View, Data.Element: Identifiable {
private struct CollectionIndex: Identifiable { var id: Int }
// MARK: - STORED PROPERTIES
private let columns: Int
private let columnsInLandscape: Int
private let vSpacing: CGFloat
private let hSpacing: CGFloat
private let vPadding: CGFloat
private let hPadding: CGFloat
private let scrollIndicators: Bool
private let axisSet: Axis.Set
private let data: [Data.Element]
private let content: (Data.Element) -> Content
// MARK: - COMPUTED PROPERTIES
private var actualRows: Int {
return data.count / self.actualColumns
}
private var actualColumns: Int {
return UIDevice.current.orientation.isLandscape ? columnsInLandscape : columns
}
// MARK: - INIT
public init(_ data: Data,
columns: Int = 2,
columnsInLandscape: Int? = nil,
scrollIndicators: Bool = true,
axisSet: Axis.Set = .vertical,
vSpacing: CGFloat = 10,
hSpacing: CGFloat = 10,
vPadding: CGFloat = 10,
hPadding: CGFloat = 10,
content: @escaping (Data.Element) -> Content) {
self.data = data.map { $0 }
self.content = content
self.columns = max(1, columns)
self.columnsInLandscape = columnsInLandscape ?? max(1, columns)
self.vSpacing = vSpacing
self.hSpacing = hSpacing
self.vPadding = vPadding
self.hPadding = hPadding
self.scrollIndicators = scrollIndicators
self.axisSet = axisSet
}
// MARK: - BODY
public var body : some View {
GeometryReader { geometry in
ScrollView(self.axisSet, showsIndicators: self.scrollIndicators) {
if self.axisSet == .horizontal {
HStack(alignment: .center, spacing: self.hSpacing) {
ForEach((0 ..< self.actualRows).map { CollectionIndex(id: $0) }) { row in
self.createRow(row.id, geometry: geometry)
}
}
} else {
VStack(spacing: self.vSpacing) {
ForEach((0 ..< self.actualRows).map { CollectionIndex(id: $0) }) { row in
self.createRow(row.id * self.actualColumns, geometry: geometry)
}
// LAST ROW HANDLING
if (self.data.count % self.actualColumns > 0) {
self.createRow(self.actualRows * self.actualColumns, geometry: geometry, isLastRow: true)
.padding(.bottom, self.vPadding)
}
}
}
}
}
}
// MARK: - HELPER FUNCTIONS
private func createRow(_ index: Int, geometry: GeometryProxy, isLastRow: Bool = false) -> some View {
HStack(spacing: self.hSpacing) {
ForEach((0 ..< actualColumns).map { CollectionIndex(id: $0) }) { column in
self.contentAtIndex(index + column.id)
.frame(width: self.contentWidthForGeometry(geometry))
.opacity(!isLastRow || column.id < self.data.count % self.actualColumns ? 1.0 : 0.0)
}
}
}
private func contentAtIndex(_ index: Int) -> Content {
// (Addressing the workaround with transparent content in the last row) :
let object = index < data.count ? data[index] : data[data.count - 1]
return content(object)
}
private func contentWidthForGeometry(_ geometry: GeometryProxy) -> CGFloat {
let hSpacings = hSpacing * (CGFloat(self.actualColumns) - 1)
let width = geometry.size.width - hSpacings - hPadding * 2
return width / CGFloat(self.actualColumns)
}
}