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:
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)
}