Hi, I'm trying to learn SwiftUI by creating a small, sandbox-type app where I can try out different concepts in SwiftUI. I have the following code:
import SwiftUI
let demos: [Demo] = [
.init(title: "Display a bottom sheet with detents", kind: .practice, view: BottomSheetPractice()),
.init(title: "Synchronize animations with matchedGeometryEffect", kind: .practice, view: NamespacePractice()),
.init(title: "Lamp challenge", kind: .challenge, view: Lamp()),
.init(title: "ScrollView Effects", kind: .practice, view: ScrollViewEffects()),
]
struct Demo<Content: View>: Identifiable
{
enum Category: String, CaseIterable, Identifiable
{
case challenge = "Challenge", practice = "Practice"
var id: Self { self }
}
let id = UUID()
let title: String
let kind: Category
let view: Content
}
But I get the error:
error: conflicting arguments to generic parameter 'Content' ('BottomSheetPractice' vs. 'NamespacePractice')
My understanding of generics is somewhat limited, so I'm hoping someone can point me in the right direction.
Another approach I've tried is to change Demo from a struct to an enum and just have computed properties for each of the data, but ideally I'd like to define each of the values together in the same location.
You can’t just have a [Demo], it has to be a [Demo<Something>]. When you say [Demo] it tries to figure out what that Something is for you, but realizes that each entry in the array has a different Something.
Without knowing more about your situation, I can only suggest the escape hatch of having Demo use AnyView instead of a generic. That may not be necessary though depending on what you’re trying to do.
@bbrk24 thanks for the response. An example use case would be roughly:
struct MainView: View
{
@State private var showingSidebar = false
@State private var current: Demo.ID? = demos[0].id
var body: some View {
ZStack(alignment: .leading) {
if showingSidebar {
SidebarView(current: $current)
}
demos.first(where: { current == $0.id })!.view
}
}
struct SidebarView: View
{
@Binding var current: Demo.ID?
func demos(for category: Demo.Category) -> [Demo]
{
demos.filter{ $0.kind == category }
}
var body: some View
{
List(selection: $current) {
ForEach(Demo.Category.allCases) { category in
Section {
ForEach(demos(for: category)) { demo in
Text(demo.title)
}
} header: {
Text(category.rawValue)
}
}
}
}
}
Instead of generics, I've also tried using 'any View' for the view field in the Demo struct and an AnyView in MainView. It builds, but doesn't show the intended view for any of the demos.
Ah yeah, I've done something similar for conditionally rendering different views for highly-customizable navigation. In my case we made the view a computed property rather than a stored one to avoid calling the init for views that aren't rendered, but we did have to use AnyView. (AnyView is behaviorally different from any View for reasons I don't fully understand.)