SwiftUI 中的可扩展列表只会扩展一次,不会收缩
Expandable List in SwiftUI only expands once and won't contract
我正在尝试(但失败了!)在 SwiftUI 中实现可扩展的问答列表。
struct FAQ: Equatable, Identifiable {
static func ==(lhs: FAQ, rhs: FAQ) -> Bool {
return lhs.id == rhs.id
}
let id = UUID()
let question: String
let answers: [String]
var isExpanded: Bool = false
}
struct ContentView: View {
@State private (set) var faqs: [FAQ] = [
FAQ(question: "What is the fastest animal on land?", answers: ["The cheetah"]),
FAQ(question: "What colours are in a rainbox?", answers: ["Red", "Orange", "Yellow", "Blue", "Indigo", "Violet"])
]
var body: some View {
List {
ForEach(faqs) { faq in
Section(header: Text(faq.question)
.onTapGesture {
if let index = self.faqs.firstIndex(of: faq) {
self.faqs[index].isExpanded.toggle()
}
}
) {
if faq.isExpanded {
ForEach(faq.answers, id: \.self) {
Text("• " + [=10=]).font(.body)
}
}
}
}
}
}
}
点击任何问题成功将答案展开,但再次点击相同的 header 不会缩小答案,点击第二个问题也不会展开这些答案。
明智地放置了一些 print
s,我可以看到 isExpanded
在点击第一个问题时切换到 true
,但之后不会切换回 false
.
谁能解释一下我做错了什么?
问题出在您的 @State var faq: [FAQ]
行上。 @State
属性 包装器允许您的视图观察数组中的更改,但更改数组元素之一的 属性 不算作数组中的更改。
您可能想要创建一个小视图模型,而不是使用状态变量,如下所示:
class ViewModel: ObservableObject {
@Published var faqs: [FAQ] = [
FAQ(question: "What is the fastest animal on land?", answers: ["The cheetah"]),
FAQ(question: "What colours are in a rainbox?", answers: ["Red", "Orange", "Yellow", "Blue", "Indigo", "Violet"])
]
}
并更新您的 ContentView:
struct ContentView: View {
@ObservedObject var model = ViewModel()
var body: some View {
List {
ForEach(model.faqs.indices) { index in
Section(header: Text(self.model.faqs[index].question)
.onTapGesture {
self.model.faqs[index].isExpanded.toggle()
}
) {
if self.model.faqs[index].isExpanded {
ForEach(self.model.faqs[index].answers, id: \.self) {
Text("• " + [=11=]).font(.body)
}
}
}
}
}
}
}
ContentView
中的 @ObservedObject
属性 包装器现在监视其 ViewModel
对象宣布的任何更改,并且 @Published
包装器告诉 ViewModel
class 宣布数组发生的任何事情,包括对其元素的更改。
通过将视图拆分为更小的独立视图,可以轻松实现这一点。
模型不需要知道答案是否显示在屏幕上。因此,无需查看数组即可知道视图是否展开。
此外,当其中一个部分展开时,不需要重新绘制整个列表。有关何时重绘视图的更多信息,请参阅 this article。
struct FAQ {
var question: String
var answers: [String]
}
struct InfoView: View {
let information: [FAQ] = [
FAQ(question: "What is the fastest animal on land?", answers: ["The cheetah"]),
FAQ(question: "What colours are in a rainbox?", answers: ["Red", "Orange", "Yellow", "Blue", "Indigo", "Violet"])
]
var body: some View {
List {
ForEach(information, id: \.question) { info in
InfoSection(info: info)
}
}
}
}
struct InfoSection: View {
let info: FAQ
@State var showsAnswer = false
var body: some View {
Section(header: questionHeader) {
if showsAnswer {
ForEach(info.answers, id: \.self) { answer in
Text("• " + answer)
}
}
}
}
var questionHeader: some View {
Text(info.question)
.foregroundColor(.primary)
.onTapGesture {
self.showsAnswer.toggle()
}
}
}
我正在尝试(但失败了!)在 SwiftUI 中实现可扩展的问答列表。
struct FAQ: Equatable, Identifiable {
static func ==(lhs: FAQ, rhs: FAQ) -> Bool {
return lhs.id == rhs.id
}
let id = UUID()
let question: String
let answers: [String]
var isExpanded: Bool = false
}
struct ContentView: View {
@State private (set) var faqs: [FAQ] = [
FAQ(question: "What is the fastest animal on land?", answers: ["The cheetah"]),
FAQ(question: "What colours are in a rainbox?", answers: ["Red", "Orange", "Yellow", "Blue", "Indigo", "Violet"])
]
var body: some View {
List {
ForEach(faqs) { faq in
Section(header: Text(faq.question)
.onTapGesture {
if let index = self.faqs.firstIndex(of: faq) {
self.faqs[index].isExpanded.toggle()
}
}
) {
if faq.isExpanded {
ForEach(faq.answers, id: \.self) {
Text("• " + [=10=]).font(.body)
}
}
}
}
}
}
}
点击任何问题成功将答案展开,但再次点击相同的 header 不会缩小答案,点击第二个问题也不会展开这些答案。
明智地放置了一些 print
s,我可以看到 isExpanded
在点击第一个问题时切换到 true
,但之后不会切换回 false
.
谁能解释一下我做错了什么?
问题出在您的 @State var faq: [FAQ]
行上。 @State
属性 包装器允许您的视图观察数组中的更改,但更改数组元素之一的 属性 不算作数组中的更改。
您可能想要创建一个小视图模型,而不是使用状态变量,如下所示:
class ViewModel: ObservableObject {
@Published var faqs: [FAQ] = [
FAQ(question: "What is the fastest animal on land?", answers: ["The cheetah"]),
FAQ(question: "What colours are in a rainbox?", answers: ["Red", "Orange", "Yellow", "Blue", "Indigo", "Violet"])
]
}
并更新您的 ContentView:
struct ContentView: View {
@ObservedObject var model = ViewModel()
var body: some View {
List {
ForEach(model.faqs.indices) { index in
Section(header: Text(self.model.faqs[index].question)
.onTapGesture {
self.model.faqs[index].isExpanded.toggle()
}
) {
if self.model.faqs[index].isExpanded {
ForEach(self.model.faqs[index].answers, id: \.self) {
Text("• " + [=11=]).font(.body)
}
}
}
}
}
}
}
ContentView
中的 @ObservedObject
属性 包装器现在监视其 ViewModel
对象宣布的任何更改,并且 @Published
包装器告诉 ViewModel
class 宣布数组发生的任何事情,包括对其元素的更改。
通过将视图拆分为更小的独立视图,可以轻松实现这一点。
模型不需要知道答案是否显示在屏幕上。因此,无需查看数组即可知道视图是否展开。
此外,当其中一个部分展开时,不需要重新绘制整个列表。有关何时重绘视图的更多信息,请参阅 this article。
struct FAQ {
var question: String
var answers: [String]
}
struct InfoView: View {
let information: [FAQ] = [
FAQ(question: "What is the fastest animal on land?", answers: ["The cheetah"]),
FAQ(question: "What colours are in a rainbox?", answers: ["Red", "Orange", "Yellow", "Blue", "Indigo", "Violet"])
]
var body: some View {
List {
ForEach(information, id: \.question) { info in
InfoSection(info: info)
}
}
}
}
struct InfoSection: View {
let info: FAQ
@State var showsAnswer = false
var body: some View {
Section(header: questionHeader) {
if showsAnswer {
ForEach(info.answers, id: \.self) { answer in
Text("• " + answer)
}
}
}
}
var questionHeader: some View {
Text(info.question)
.foregroundColor(.primary)
.onTapGesture {
self.showsAnswer.toggle()
}
}
}