Are there maximum limits to VStack? [duplicate]
Asked Answered
S

4

39

I started with a clean project and added 5 buttons and 5 spacers in a VStack and all is good. When I add the 6th spacer at the bottom, the code suddenly won't compile with the error: "Ambiguous reference to member 'buildBlock()'".

What is causing this error? Is this a bug related to SwiftUI? Or is it a feature? It's not the first time I notice that VStack or HStack is limited in the number of entries, is there some documentation around this?

Not exactly confidence inspiring, should I switch back to UIKit?

Selfseeker answered 15/10, 2019 at 15:17 Comment(0)
H
43

SwiftUI uses ViewBuilder to construct the views that make up many SwiftUI views, like VStack, HStack, List, etc. If you take a look at the ViewBuilder documentation, you'll see that the buildBlock function has many copies, each with a different amount of views as arguments. The function with the most amount of views only takes in 10 views which is why you are seeing the limitation that you observed. A way to work around this is by using Groups:

VStack {
    Group {
        Text("Placeholder 0")
        Text("Placeholder 1")
        Text("Placeholder 2")
        Text("Placeholder 3")
        Text("Placeholder 4")
        Text("Placeholder 5")
        Text("Placeholder 6")
        Text("Placeholder 7")
        Text("Placeholder 8")
        Text("Placeholder 9")
    }
    Group {
        Text("Other Placeholder 10")
        Text("Other Placeholder 11")
        Text("Other Placeholder 12")
        Text("Other Placeholder 13")
        Text("Other Placeholder 14")
        Text("Other Placeholder 15")
        Text("Other Placeholder 16")
        Text("Other Placeholder 17")
        Text("Other Placeholder 18")
        Text("Other Placeholder 19")
    }
}

Although if you want 20 views that are really similar to each other, it is encouraged to use something like a ForEach to avoid making your views too bloated. The above workaround should only be used if the >10 views are truly unique. Even then, a more SwiftUI-y method would be to split up these views into more smaller views:

VStack {
    SingleDigitPlaceholders()
    TeensPlaceholders()
}

struct SingleDigitPlaceholders: View {
    var body: some View {
        ForEach(0..<10) { i in
            Text("Placeholder \(i)")
        }
    }
}
struct TeensPlaceholders: View {
    var body: some View {
        ForEach(10..<20) { i in
            Text("Other Placeholder \(i)")
        }
    }
}

Of course, in this specific example, you can just have the two ForEachs in the original view, but in more complex cases, the point still stands. For example, in a form with many elements (e.g. in a job application form: first name, last name, address, phone number text fields, education dropdown menus, date fields, etc.) you can still split up one view into smaller components (in the job application example - a personal information view, an educational information view, etc.).

Hopple answered 15/10, 2019 at 15:41 Comment(0)
D
15

You can have at most 10 children in your VStack (and ZStack, HStack, and so forth). This is strictly related to their implementation and to the implementation of the @ViewBuilder closures in general. Look at the interfaces here below (you can find them through Xcode, I slightly simplified them in order to be more readable):

public struct ViewBuilder {    
    /// Builds an empty view from an block containing no statements, `{ }`.
    public static func buildBlock() -> EmptyView

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

extension ViewBuilder {    
    public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)> where C0 : View, C1 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2) -> TupleView<(C0, C1, C2)> where C0 : View, C1 : View, C2 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3) -> TupleView<(C0, C1, C2, C3)> where C0 : View, C1 : View, C2 : View, C3 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3, C4>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4) -> TupleView<(C0, C1, C2, C3, C4)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3, C4, C5>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5) -> TupleView<(C0, C1, C2, C3, C4, C5)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6) -> TupleView<(C0, C1, C2, C3, C4, C5, C6)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View, C8 : View
}

extension ViewBuilder {
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View, C8 : View, C9 : View
}

As you can see you can build those kind of views with at most 10 children. Each buildBlock method takes an exact number of views as parameters. This is because @ViewBuilder closures, at least at the moment, don't support variadic arguments.

A workaround can be:

struct ContentView: View {
    var body: some View {
        VStack {
            Group {
                //10 views here
            }
            
            Group {
                //10 views here
            }
        }
    }
}
Dahlia answered 15/10, 2019 at 15:28 Comment(1)
I added the Group around my views, I'm still getting the Extra argument in call error.Scoutmaster
E
3

Here is a ViewBuilder extension that supports up to an additional 20 views.

import SwiftUI

extension ViewBuilder {

    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13, _ c14: C14
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13, C14)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View, C14: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13, c14)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13, _ c14: C14, _ c15: C15
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13, C14, C15)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View, C14: View, C15: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13, c14, c15)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13, _ c14: C14, _ c15: C15, _ c16: C16
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13, C14, C15, C16)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View, C14: View, C15: View, C16: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13, c14, c15, c16)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13, _ c14: C14, _ c15: C15, _ c16: C16, _ c17: C17
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13, C14, C15, C16, C17)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View, C14: View, C15: View, C16: View, C17: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13, c14, c15, c16, c17)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13, _ c14: C14, _ c15: C15, _ c16: C16, _ c17: C17, _ c18: C18
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13, C14, C15, C16, C17, C18)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View, C14: View, C15: View, C16: View, C17: View, C18: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13, c14, c15, c16, c17, c18)) }
        ))
    }
    
    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18, C19>(
        _ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9, _ c10: C10, _ c11: C11, _ c12: C12, _ c13: C13, _ c14: C14, _ c15: C15, _ c16: C16, _ c17: C17, _ c18: C18, _ c19: C19
    ) -> TupleView<
        (
            Group<TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>>,
            Group<TupleView<(C10, C11, C12, C13, C14, C15, C16, C17, C18, C19)>>
        )
    >
    where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View, C10: View, C11: View, C12: View, C13: View, C14: View, C15: View, C16: View, C17: View, C18: View, C19: View {
        TupleView((
            Group { TupleView((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) },
            Group { TupleView((c10, c11, c12, c13, c14, c15, c16, c17, c18, c19)) }
        ))
    }
}
Estrogen answered 16/3, 2022 at 20:13 Comment(0)
S
1

I have 6 views after added the Group around my views, I'm still getting the Extra argument in call error.

So this is what I did to fix the error.

struct ContentView: View {
    var body: some View {
        VStack {
            Group {
                //3 views here
            }

            Group {
                //3 views here
            }
        }
    }
}
Scoutmaster answered 12/3, 2021 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.