Home > front end >  How to add a generic SwiftUI view to another view?
How to add a generic SwiftUI view to another view?

Time:09-01

When trying to use a generic type to init/add a view to another SwiftUI view, I get the error Type 'T' has no member 'init'. How can this be solved?

I guess the problem is, that View is just a protocol which only requires a body property and nothing more. But what type could be used instead?

Simple Example:

Trying to use TrippleView<T: View> which should create/show three views of type T. Adding views (RedRectView or BlueCircleView) is no problem. Passing these views as generic parameter fails:

struct SomeView: View {
    var body: some View {
        TrippleView<RedRectView>()
        TrippleView<BlueCircleView>()
    }
}

struct TrippleView<T: View>: View {
    var body: some View {
        VStack {
            // Does NOT work: Type 'T' has no member 'init'
            T()
            T()
            T()
            
            // Works fine
            // RedRectView()
            // BlueCircleView()
            // RedRectView()
        }
    }
}

struct RedRectView: View {...}
struct BlueCircleView: View {...}

EDIT:

The TrippleView is of course just an example. I would like to use the generic view just as any other generic type: To a common base "type" in different scenarios.

For example two versions of a list view which use a generic CellView to display the same data in two different styles / layouts.

CodePudding user response:

You need a ViewBuilder inside your struct to initialize the view. Here's an example,

Struct that takes in generic view and returns a view.

struct TripleView<T: View>: View {
// simple example that takes in one parameter.
var someView: T

init(@ViewBuilder someView: () -> T) {
    self.someView = someView()
}

var body: some View {
    // You can modify your viewes here. 
    someView
        .foregroundColor(.red)
    someView
        .foregroundColor(.blue)
    someView
        .foregroundColor(.green)
    }
}

Or you can take in multiple parameters

struct TripleViewsWithParam<T1: View, T2: View, T3: View>: View {
// you can take as many parameters as you want.
var someView1: T1
var someView2: T2
var someView3: T3

init(@ViewBuilder someView1: () -> T1, @ViewBuilder someView2: () -> T2, @ViewBuilder someView3: () -> T3) {
    self.someView1 = someView1()
    self.someView2 = someView2()
    self.someView3 = someView3()
}

var body: some View {
    VStack {
        someView1
        someView2
        someView3
    }
}
}

Calling the struct in your main view

struct SomeView: View {
var body: some View {
    VStack{
        TripleView {
            Text("Triple View Example")
        }
        TripleViewsWithParam(
            someView1: {
                Text("View 1")
            }, someView2: {
                Text("View 2")
            }, someView3: {
                Text("View 3")
            }
        )
    }

}
}

The result

  • Related