Home > Blockchain >  Solving syntax issue working with TupleView where Content == TupleView<(Content1)> or Content
Solving syntax issue working with TupleView where Content == TupleView<(Content1)> or Content

Time:11-09

This issue is one of my old issues which I am still working on it to solve. My current workaround is to not use TupleView, like this:

init(content: @escaping () -> Content) {
    self.init(count: 1, content: content)
}

Here is the issue when I got only 1 view to work:

 struct CustomView<Content: View>: View {
    
    let count: Int
    let content: () -> Content
    
    private init(count: Int, content: @escaping () -> Content) {
        self.count = count
        self.content = content
    }
    
    // Here: is the issue!!!   // <<: Here
    init<Content1: View>(@ViewBuilder content: @escaping () -> Content) where Content == TupleView<(Content1)> {
        self.init(count: 1, content: content)
    }

    
    init<Content1: View, Content2: View>(@ViewBuilder content: @escaping () -> Content) where Content == TupleView<(Content1, Content2)> {
        self.init(count: 2, content: content)
    }

    // ... init for 3,4,5,6,7,8,9,10

    var body: some View {
        
        content()
            .onAppear() { print(count) }
        
    }
    
}

My goal is to solve the initialization issue for just for 1 view, with same syntax that I use for 2 or more views.

Here's a use case:

struct ContentView: View {
    var body: some View {
        
        CustomView(content: {

            Text("Hello, world!")
            //Text("Hello, world!")


        })

    }
}

But Xcode throws these errors:

Generic parameter 'Content1' could not be inferred

Cannot convert value of type 'Text' to closure result type 'TupleView<(Content1)>'

The code works well for 2, 3, and even 10 views, but I have issues when it's just 1!

As I said, I have found a workaround for my issue as I mentioned at the top of this post. But, I want to solve the errors that I get and how to use TupleView with the correct syntax.

update:

My final goal was and is to have this initializations:

enter image description here

CodePudding user response:

The ViewBuilder does not create TupleView for single view content - it just returns it as-is. See below API contract:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@resultBuilder public struct ViewBuilder {

    ...

    /// Passes a single view written as a child view through unmodified.
    ///
    /// An example of a single view written as a child view is
    /// `{ Text("Hello") }`.
    public static func buildBlock<Content>(_ content: Content) -> Content where Content : View
}

so it is impossible to match expression

@ViewBuilder content: @escaping () -> Content) where Content == TupleView<(Content1)>

for single view, because it tries ConcreteView == TupleView<(ConcreteView)>, which is impossible.

So trying to use ViewBuilder for single view in your case is wrong. And so your first variant is not a workaround, but real correct solution, because explicitly matches single view as Content in input builder closure.

Update: let me assume that you want to have only TupleView as input in designated init, then you can use below (tested with Xcode 13 / iOS 15)

init<Content1: View>(content: @escaping () -> Content1) where Content == TupleView<(Content1)> {
    self.init(count: 1, content: { TupleView(content()) })
}

CodePudding user response:

Is TupleView even defined for a single view? What would be the point of it if the view itself could just be used directly.

init(content: @escaping () -> Content ) 
{
    self.init(count: 1, content: content)
}
  • Related