Dilemma about enum with associated values and protocols

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.