Both methods will do what you want. The enum is itself a (hard-coded) "list of all such types", and you can add computed properties that handle each concrete type internally. Here's a starting point for this approach (typed out in haste and untested, but the principle should be clear enough):
import SwiftUI
// Define option types
struct TextOptions {
var text: String
}
struct PhotoOptions {
var photoName: String
}
struct VideoOptions {
var videoURL: URL
}
// The general-purpose enum
enum AnyOptions {
case text(TextOptions)
case photo(PhotoOptions)
case video(VideoOptions)
// Return the appropriate subview
@ViewBuilder
var view: some View {
switch self {
case .text(let options):
TextOptionsView(options: options)
case .photo(let options):
PhotoOptionsView(options: options)
case .video(let options):
VideoOptionsView(options: options)
}
}
}
// Container view
struct OptionsContainerView: View {
@Binding var anyOptions: AnyOptions
var body: some View {
VStack {
anyOptions.view
}
.padding()
.border(Color.gray, width: 1)
}
}
// Subviews
struct TextOptionsView: View {
@Binding var options: TextOptions
var body: some View {
VStack {
Text("Text Options")
TextField("Enter text", text: $options.text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
}
struct PhotoOptionsView: View {
@Binding var options: PhotoOptions
var body: some View {
VStack {
Text("Photo Options")
Text("Photo Name: \(options.photoName)")
}
}
}
struct VideoOptionsView: View {
@Binding var options: VideoOptions
var body: some View {
VStack {
Text("Video Options")
Text("Video URL: \(options.videoURL.absoluteString)")
}
}
}
// Use them like this:
struct ContentView: View {
@State private var currentOptions: AnyOptions = .text(TextOptions(text: "Default text"))
var body: some View {
OptionsContainerView(anyOptions: $currentOptions)
.frame(width: 300, height: 200)
}
}
The idea is that you can progressively refine this. Use subviews to provide UI for features that are common to a subset of the option types, and add computed properties to AnyOptions that return either the subview or a HiddenView(), as required. For example, a widget that configures thumbnail creation makes sense for photos and video, but text options wouldn't need a subview for that.
This approach is very quick and clear for relatively simple use cases, but the bindings can get hairy very quickly. If you want a truly generic, extensible framework for dynamically configurable views, you're probably better off using protocols.