Hello im still in my beginning of learning SwiftUI.
Currently I try to build a expense tracker myself for some more advanced practice.
For that reason I try to replicate a excel like pivot table for the Mac. Therefore by starting to create a custom table in the first place. The standard SwiftUI Table has this cool straight forward functionality where you can define the TableColumns with a title and a function (data) -> (View) . This is the point where I am stuck now for quite a while and decided to now ask for some help.
My problem is I have no clue how to store and retrieve those column creating functions without creating a bunch of copies of the same functions where i just change the amount of generics used. I also want to avoid using AnyViews.
My approche until now was trying to store them in some generic Tuple that i then try to disasamble via function overloading to get to the function((data) -> View) which gives me the view for each column-cell. Didn’t even get this one working. (Possible that i get the concept of generics wrong)
So first I have some type of TableColumn:
protocol columnContent {
associatedtype Body: View
associatedtype Data: Identifiable
func getView(data: Data) -> Body
}
struct Column<Data, Content>: columnContent where Data: Identifiable, Content: View {
typealias Body = Content
let body: (Data) -> Content
func getView(data: Data) -> Content {
body(data)
}
}
That I then gave to a my custom Table Object. Basically just looping over the data and stacking the Rows:
struct MyTable<Data, T>: View where Data: Identifiable {
let columnTuple: T
let data: [Data]
init(
data: [Data],
@ColumnBuilder columns: () -> T
) {
self.data = data
self.columnTuple = columns()
}
var body: some View {
ForEach(data) { d in
VStack {
getTableRowView(data: d)
}
}
}
func getTableRowView(data: Data) -> some View{
extractViews(columnTuple, data: data) // Error: Instance method 'extractViews(_:data:)' requires that 'T' conform to 'columnContent'
}
func extractViews<c1>(_ t: (c1), data: Data) where c1: columnContent, c1.Data == Data {
VStack {
t.getView(data: data)
}
}
// needs to be created for a varity, manually
func extractViews<c1,c2>(_ t: (c1,c2), data: Data) where c1: columnContent, c2: columnContent, c1.Data == c2.Data, c1.Data == Data {
SomeTableRowViewLayout { // could be a HStack for simplification
t.0.getView(data: data)
t.1.getView(data: data)
}
}
}
As a resultBuilder I used this. It just puts the closures creating the cellViews in a Tuple.
@resultBuilder
struct ColumnBuilder {
// needs to be created for a varity, manually
static func buildBlock<c1,c2>(_ C1: c1, _ C2: c2) -> (c1,c2) where c1: columnContent, c2: columnContent, c1.Data == c2.Data {
return (C1,C2)
}
// static func buildBlock<c1,c2,c3>(_ C1: c1, _ C2: c2, _ C3: c3) -> (c1,c2, c3)
// static func buildBlock<c1,c2,c3,c4>(_ C1: c1, _ C2: c2, _ C3: c3, _ C4: c4) -> (c1,c2, c3, c4)
// ...
}
All in all i tried to find out how apple has done this with their version out of the given documentation. Is there any way i can say that the elements in the Tuple (independent of the length) all conform to columnContent? Is using tuples here even the right approche? and how can i generalize this so i dont have to create that many function overloads?
Tom is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1