ForEach(sections:) and Group(sections:) decompose a @ViewBuilder content view into its
Sections, so a custom container can lay out each header, content, and footer its own way.
This continues the subview decomposition APIs from the basics doc.
Enumerate sections with ForEach(sections:)
Callers write plain Section(header:) and Section(footer:) blocks. The container receives each
one as a SectionConfiguration: it is Identifiable and exposes header, footer, and
content, each a SubviewsCollection.
struct Carousel<Content: View>: View {
@ViewBuilder var content: Content
var body: some View {
ScrollView {
LazyVStack {
ForEach(sections: content) { section in
VStack {
section.header
ScrollView(.horizontal) {
LazyHStack {
section.content
.containerRelativeFrame(.horizontal)
.frame(minHeight: 100)
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)
.contentMargins(16)
section.footer
}
}
}
}
}
}
section.content is a collection, so you can also iterate it with an inner
ForEach(section.content) or wrap it in any stack.
Index sections with Group(sections:)
Group(sections:) hands over the whole SectionCollection, a RandomAccessCollection of
SectionConfiguration values. Use it to single out the first section and slice the rest.
Group(sections: content) { sections in
if !sections.isEmpty {
sections[0].content.frame(minHeight: 100)
}
if sections.count > 1 {
ForEach(sections[1...]) { section in
section.header
// horizontal rail per section
section.footer
}
}
}
Content without sections still works
When the caller passes plain views with no Section, SwiftUI treats the whole content as one
section, so the section APIs never see an empty hierarchy. Guard the indexed access anyway, as
above, because the caller controls the count.