SwiftUI 中的绑定无法在循环内工作
Binding in SwiftUI fails to work inside of a loop
根据我的理解,我编写了以下代码来展开/折叠列表中的一个部分。
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()
}
}
}
这按预期工作。但是,以下代码不起作用。我所做的只是将多个部分替换为 ForEach
。这段代码的行为应该是相同的,但是当我点击 headers 部分时没有任何反应。我错过了什么?好像绑定不起作用。
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)
}
}
}
}
}
}
}
}
这是由于 statically_ranged_ForEach...正如我在 SO 上所经历的那样,这是 SwiftUI 中最令人困惑的概念。无论如何 - 解决方案是使用 dynamic部分显式模型的容器。
这是您的代码的简化工作演示(但这个想法应该很容易被您未提供的组件采用)。
测试 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)
}
}
}
}
}
}
}
}
根据我的理解,我编写了以下代码来展开/折叠列表中的一个部分。
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()
}
}
}
这按预期工作。但是,以下代码不起作用。我所做的只是将多个部分替换为 ForEach
。这段代码的行为应该是相同的,但是当我点击 headers 部分时没有任何反应。我错过了什么?好像绑定不起作用。
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)
}
}
}
}
}
}
}
}
这是由于 statically_ranged_ForEach...正如我在 SO 上所经历的那样,这是 SwiftUI 中最令人困惑的概念。无论如何 - 解决方案是使用 dynamic部分显式模型的容器。
这是您的代码的简化工作演示(但这个想法应该很容易被您未提供的组件采用)。
测试 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)
}
}
}
}
}
}
}
}