KevinP
KevinP

Reputation: 2738

SwiftUI - Can I define an empty initializer for a generic view?

I have a generic view with an optional @ViewBuilder. I want to have two initializers, one is responsible for setting the @ViewBuilder and another which should serve as an empty default.

struct OptionalViewBuilder<Content: View>: View {
    
    let content: (() -> Content)?
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    init() {
        self.content = nil
    }
    
    var body: some View {
        VStack {
            Text("Headline")
            Divider()
            content?()
        }
    }
}

If I initialize the view with the ViewBuilder parameter, it works of course. However, when I use the empty initializer, I get the error message:

Generic parameter 'Content' could not be inferred

struct ContentView: View {
    var body: some View {
        OptionalViewBuilder {
            Text("Text1")
            Text("Text2")
        }
        OptionalViewBuilder() // <-- Generic parameter 'Content' could not be inferred
    }
}

I know that Swift needs explicit types at runtime, but is there a way to make the default initializer work anyway?

Upvotes: 5

Views: 2168

Answers (3)

orafaelreis
orafaelreis

Reputation: 2881

You also can pass EmptyView() as default parameter

struct OptionalViewBuilder<Content: View>: View {
    
   let content: () -> Content?

   init(@ViewBuilder content: @escaping () -> Content? = { EmptyView() }) {
        self.content = content
    }
}

Upvotes: -1

New Dev
New Dev

Reputation: 49590

Because OptionalViewBuilder is generic with respect to its Content type, you'd need to define that type as something in your default case. This could be EmptyView, if you don't want to render anything.

To do that, you'd need an init which is constrained to Content == EmptyView, and the content property need not be optional:

struct OptionalViewBuilder<Content: View>: View {
    
    let content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    init() where Content == EmptyView {
        self.init(content: { EmptyView() })
    }
    
    var body: some View {
        VStack {
            Text("Headline")
            Divider()
            content()
        }
    }
}

Upvotes: 7

John Nimis
John Nimis

Reputation: 606

I can make the compile error go away by adding an empty closure, like this:

struct ContentView: View {
    var body: some View {
        OptionalViewBuilder {
            Text("Text1")
            Text("Text2")
        }
        OptionalViewBuilder() {} // I added an empty closure
    }
}

Does this get you what you need?

Upvotes: 1

Related Questions