I have a view with a List and depending on the item of the list I have clicked on, I need to open a different NavigationLink.
I have a row model for one row of the List (I'm saying List even though I'm actually usually using a custom made element called Collection, which is a List with HStack and VStack together to emulate the UICollectionView)
In the row model view I can only specify one destination for the NavigationLink.
A solution I can think of is getting the index of the item of the list that was clicked and opening a certain view based on that. But I was not able to get it to work.
I basically need that each element in the List opens a different view.
Any help is appreciated! Thank you ! :)
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)
}
}