Premise
I’m currently trying to build out a SwiftUI View to display cells of various sizes, each containing some content view struct that I want to additionally conform to a custom protocol.
Container Views will hold these content views, and will require a closure returning any View & CustomProtocol
which I’ve been able to implement with concrete types.
In addition to just displaying these cells, I want the content inside cells to be selectable by the user, and to store those selections. For this I've tried a couple different methods to store these values, but in both attempts, I have not been able to convert the stored values into concrete instances of a conforming content view.
Issues
As for the errors I am encountering, both are at the final @ViewBuilder funcs, where I'm experiencing branch-mismatch on the switch statement, or nonconformance of switch to View without @ViewBuilder. I'm also experiencing vague errors about the use of some, any, and protocols on their own in the first func. I understand why I’m experiencing branch mismatch on a switch statement, and vaguely understand the concept behind any Protocol
not conforming to Protocol
. To the best of my understanding, the switch statement func is the closest-to-correct execution I have, and a custom resultBuilder
might be the answer, but I’m in over my head with those at the moment, and have not been able to successfully implement my own
Context to Code
I will include a minimum replicable code sample below outlining my design. The protocol Secondary
represents my custom protocol, followed by an example content view, container cell view, and finally the main view. The main view’s struct also includes two funcs attempting to convert stored value to struct instances.
Minimum Replicable Code
import SwiftUI
protocol Secondary {
associatedtype Assoc: Secondary & View
var height: CGFloat { get }
var tint: Color { get }
init()
init(_ assoc: Assoc, height: CGFloat?)
func height(_ height: CGFloat) -> Assoc
}
struct TestCellContent: View, Secondary {
typealias Assoc = TestCellContent
var height: CGFloat
var tint: Color
init() {
self.height = 150
self.tint = .blue
}
init(_ assoc: Self, height: CGFloat? = nil) {
self.tint = assoc.tint
self.height = height ?? assoc.height
}
var body: some View {
Text("Example Content")
.foregroundStyle(tint)
.frame(height: height)
}
func height(_ height: CGFloat) -> Self {
.init(self, height: height)
}
}
struct InfoCellContent: View, Secondary {
typealias Assoc = InfoCellContent
var height: CGFloat
var tint: Color
init() {
self.height = 120
self.tint = .blue
}
init(_ assoc: Self, height: CGFloat? = nil) {
self.tint = assoc.tint
self.height = height ?? assoc.height
}
var body: some View {
Text("Informative Content")
.foregroundStyle(tint)
.frame(height: height)
}
func height(_ height: CGFloat) -> Self {
.init(self, height: height)
}
}
struct SmallContainerCell<Content: View & Secondary>: View {
var height: CGFloat = 100
var content: Content
init(_ content: @escaping () -> Content) {
self.content = content()
}
var body: some View {
content
.height(height)
}
}
enum CellType {
case test, info
}
struct Storage: Identifiable {
var id: UUID = UUID()
var content: any Secondary & View
var conentType: CellType
}
struct SummaryView: View {
@State private var cells: [Storage]
var body: some View {
VStack {
ForEach(cells) { cell in
VStack {
SmallContainerCell {
makeCell(for: cell)
}
SmallContainerCell {
makeCell(for: cell.conentType)
}
}
}
}
}
@ViewBuilder func makeCell(for input: Storage) -> some Secondary & View {
input.content
}
@ViewBuilder func makeCell(for type: CellType) -> some Secondary & View {
switch type {
case .test:
TestCellContent()
case .info:
InfoCellContent()
}
}
}