2

I am trying to build a VStack (container) containing multiple VStacks (element) with each a title and a text. The element VStacks should be distributed equally and have a spacer in between.

For some reason it only works up to 4 element VStack and if I increase then I will get an error

Failed to build ContentView.swift
Ambiguous reference to member 'buildingBlock()'

Here is my code:

import SwiftUI

struct ContentView: View {
    var body: some View {

        VStack {
            Spacer()
            VStack {
                Text("Title 1")
                Text("Text 1")
            }
            Spacer()
            VStack {
                Text("Title 2")
                Text("Text 2")
            }
            Spacer()
            VStack {
                Text("Title 3")
                Text("Text 3")
            }
            Spacer()
            VStack {
                Text("Title 4")
                Text("Text 4")
            }
            Spacer()
            VStack {
                Text("Title 5")
                Text("Text 5")
            }
            Spacer()
            VStack {
                Text("Title 6")
                Text("Text 6")
            }
            Spacer()
            VStack {
                Text("Title 7")
                Text("Text 7")
            }
            Spacer()
            VStack {
                Text("Title 8")
                Text("Text 8")
            }
            Spacer()
            VStack {
                Text("Title 9")
                Text("Text 9")
            }
            Spacer()
            VStack {
                Text("Title 10")
                Text("Text 10")
            }
            Spacer()'
        }
        .background(Color.red)

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
FrankZp
  • 1,996
  • 3
  • 21
  • 36

1 Answers1

3

Because of the way SwiftUI is implemented, a view can only have at most 10 direct, explicit subviews. Your top-level VStack has 21 direct subviews: the 10 child VStacks, and the 11 Spacers around them.

There are various workarounds. If you really need a hard-coded list of subviews, you can use Group to make some children indirect:

struct ContentView: View {
    var body: some View {

        VStack {
            Group {
                Spacer()
                VStack {
                    Text("Title 1")
                    Text("Text 1")
                }
                Spacer()
                VStack {
                    Text("Title 2")
                    Text("Text 2")
                }
                Spacer()
                VStack {
                    Text("Title 3")
                    Text("Text 3")
                }
            }
            Group {
                Spacer()
                VStack {
                    Text("Title 4")
                    Text("Text 4")
                }
                Spacer()
                VStack {
                    Text("Title 5")
                    Text("Text 5")
                }
                Spacer()
                VStack {
                    Text("Title 6")
                    Text("Text 6")
                }
            }
            Group {
                Spacer()
                VStack {
                    Text("Title 7")
                    Text("Text 7")
                }
                Spacer()
                VStack {
                    Text("Title 8")
                    Text("Text 8")
                }
                Spacer()
                VStack {
                    Text("Title 9")
                    Text("Text 9")
                }
            }
            Spacer()
            VStack {
                Text("Title 10")
                Text("Text 10")
            }
            Spacer()
        }
        .background(Color.red)
    }
}

Here, the top-level VStack has 6 direct children: 3 Group, 2 Spacer, and 1 VStack. Each Group also has 6 direct children: 3 VStack and 3 Spacer. No view has more than 10 direct children.

A better approach here is to use ForEach to generate the children:

struct ContentView: View {
    var body: some View {

        VStack {
            Spacer()
            ForEach(1 ... 10, id: \.self) { i in
                Group {
                    VStack {
                        Text("Title \(i)")
                        Text("Text \(i)")
                    }
                    Spacer()
                }
            }
        }
        .background(Color.red)
    }
}

Now the top-level VStack only has two direct subviews: the first Spacer and the ForEach. The ForEach replicates its subview (the Group) as many times as needed.

We use the Group in this case to allow Swift to infer the type of the ForEach body, since the ForEach body would otherwise contain two statements (the VStack and the Spacer) which would prevent Swift from inferring its type.

rob mayoff
  • 342,380
  • 53
  • 730
  • 766
  • thanks for the detailed and very interesting reply. Can you please add a bit more about why SwiftUI only allows 10 direct subviews? I think that is quite limiting. – FrankZp Sep 24 '19 at 06:57
  • 1
    I explained it [in this answer](https://stackoverflow.com/a/57173445/77567). Search for “understand”. – rob mayoff Sep 24 '19 at 07:12