Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there maximum limits to VStack?

Tags:

swiftui

vstack

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?

like image 299
Bathan Avatar asked Oct 15 '19 15:10

Bathan


People also ask

How many views can HStack SwiftUI have?

HStack can contain up to 10 static views, if you need more static views, you can nest HStack inside another HStack or Group to nest views inside.

What does VStack mean?

vstack() function. The vstack() function is used to stack arrays in sequence vertically (row wise). This is equivalent to concatenation along the first axis after 1-D arrays of shape (N,) have been reshaped to (1,N). This function makes most sense for arrays with up to 3 dimensions.

What is VStack in Swift?

The VStack allows you to stack views vertically, from top to bottom. You can further customize the view by adding alignment or spacing to the VStack. VStack(alignment: .

What is VStack and HStack?

HStack positions views in a horizontal line, VStack positions them in a vertical line, and ZStack overlays views on top of one another.


3 Answers

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.).

like image 110
RPatel99 Avatar answered Oct 21 '22 05:10

RPatel99


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
            }
        }
    }
}
like image 44
matteopuc Avatar answered Oct 21 '22 04:10

matteopuc


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
            }
        }
    }
}
like image 39
JIANG Avatar answered Oct 21 '22 05:10

JIANG