swiftui

How to query number of views in contents for a SwiftUI Container View


I'm curious, is there a way to find out how many views are being passed in as the contents of a SwiftUI container view?

This code below is based on the sample code Apple provides for Creating custom container views. I would simply like the text at the top of the view that lists the number of views in the group to not be hard coded.

struct DisplayBoardV2<Content: View>: View {
    @ViewBuilder var content: Content

    var body: some View {
        VStack {
            // I don't want "2" to be hard-coded
            let subviewCount = 2
            
            Text("There are \(subviewCount) items:")
            ForEach(subviews: content) { subview in
                subview
            }
        }
    }
}

#Preview {
    DisplayBoardV2 {
        Section("Section 1") {
            Text("Item A")
        }
        Section("Section 2") {
            Text("Item B")
        }
    }
}

The code results in something like this:

Resulting UI

Is there a good way to look into content? Can we tap into whatever ForEach(subviews:) is doing?


Solution

  • Instead of ForEach(subviews:), use Group(subviews:).

    struct DisplayBoardV2<Content: View>: View {
        let content: Content
        
        init(@ViewBuilder content: () -> Content) {
            self.content = content()
        }
    
        var body: some View {
            VStack {
                Group(subviews: content) { subviews in
                    let subviewCount = subviews.count // <----
                    Text("There are \(subviewCount) items:")
                    ForEach(subviews) { subview in
                        subview
                    }
                }
            }
        }
    }
    

    If you want to count the views in each section's body, excluding the headers and footers, you can use Group(sections:),

    struct DisplayBoardV2<Content: View>: View {
        let content: Content
        
        init(@ViewBuilder content: () -> Content) {
            self.content = content()
        }
    
        var body: some View {
            VStack {
                Group(sections: content) { sections in
                    let subviewCount = sections.reduce(0) { $0 + $1.content.count }
                    Text("There are \(subviewCount) items:")
                    ForEach(sections) { section in
                        section.header
                        section.content
                        section.footer
                    }
                }
            }
        }
    }