Reputation: 5183
For my understanding, I've written the following code which expands / collapses a section inside of a list.
struct WORKING_CollapsableListView: View {
@State var sectionExpansionStates = [true, true, true]
var body: some View {
VStack {
List {
Section(header: CollapsableSectionHeader(expansionState: self.$sectionExpansionStates[0])) {
if self.sectionExpansionStates[0] {
ForEach(0..<10) { item in
Text("\(item) is \(self.sectionExpansionStates[0] ? "Expanded" : "Collapsed")")
.frame(height: self.sectionExpansionStates[0] ? 10 : 10)
}
}
}
Section(header: CollapsableSectionHeader(expansionState: self.$sectionExpansionStates[1])) {
if self.sectionExpansionStates[1] {
ForEach(0..<10) { item in
Text("\(item) is \(self.sectionExpansionStates[1] ? "Expanded" : "Collapsed")")
.frame(height: self.sectionExpansionStates[1] ? 10 : 10)
}
}
}
Section(header: CollapsableSectionHeader(expansionState: self.$sectionExpansionStates[2])) {
if self.sectionExpansionStates[2] {
ForEach(0..<10) { item in
Text("\(item) is \(self.sectionExpansionStates[2] ? "Expanded" : "Collapsed")")
.frame(height: self.sectionExpansionStates[2] ? 10 : 10)
}
}
}
}
}
}
}
struct CollapsableSectionHeader: View {
@Binding var expansionState: Bool
var body: some View {
Button(action: {
self.expansionState.toggle()
}) {
Text("HEADER: \(expansionState ? "Expanded" : "Collapsed")")
.bold()
}
}
}
This works as expected. However the following code does NOT work. All I've done is replaced the multiple sections with a ForEach
. This code should be identical in its behavior, but nothing happens when I tap on the section headers. What am I missing? It's as though the binding isn't working.
struct NOT_WORKING_CollapsableListView: View {
@State var sectionExpansionStates = [true, true, true]
var body: some View {
VStack {
List {
ForEach(0 ..< 3) { section in
Section(header: CollapsableSectionHeader(expansionState: self.$sectionExpansionStates[section])) {
if self.sectionExpansionStates[section] {
ForEach(0..<10) { item in
Text("\(item) is \(self.sectionExpansionStates[section] ? "Expanded" : "Collapsed")")
.frame(height: self.sectionExpansionStates[section] ? 10 : 10)
}
}
}
}
}
}
}
}
Upvotes: 1
Views: 130
Reputation: 257493
It is due to statically_ranged_ForEach... as I experienced here on SO it is most confused concept in SwiftUI.. anyway - the solution is to use dynamic container of explicit models for sections.
Here is simplified working demo of your code (but the idea should be easily adoptable to your not provided components).
Tested with Xcode 11.4 / iOS 13.4
// simple demo model for sections
struct SectionModel: Identifiable {
let id: Int
var expanded = true
}
struct TestCollapsableListView: View {
// dynamic container with model, state is triggered
@State var sections = [SectionModel(id: 0), SectionModel(id: 1), SectionModel(id: 2)]
var body: some View {
VStack {
List {
ForEach(sections) { section in
Section(header: Button("Section \(section.id)") { self.sections[section.id].expanded.toggle() }) {
if section.expanded {
ForEach(0..<10) { item in
Text("\(item) is \(section.expanded ? "Expanded" : "Collapsed")")
.frame(height: section.expanded ? 10 : 10)
}
}
}
}
}
}
}
}
Upvotes: 2