我正在尝试(并且失败!)在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("• " + $0).font(.body)
}
}
}
}
}
}
}
点击任何问题都会成功地将答案扩展到视图中,但是再次点击同一标题不会缩小答案,也不会点击第二个问题来扩展答案。
在明智地放置print
的情况下,我可以看到在第一个问题上isExpanded
被切换为true
,但是不会切换回false
。
有人可以解释我在做什么错吗?
答案 0 :(得分:1)
问题出在您的@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("• " + $0).font(.body)
}
}
}
}
}
}
}
您的@ObservedObject
中的ContentView
属性包装器现在监视其ViewModel
对象宣布的更改,而@Published
包装器通知ViewModel
类进行宣布数组发生的任何事情,包括对其元素的更改。
答案 1 :(得分:0)
通过将视图分为较小的自包含视图,可以轻松实现。
模型不需要知道答案是否在屏幕上显示。因此,不需要查看数组即可知道视图是否已扩展。
此外,扩展其中一个部分时,无需重新绘制整个列表。有关重新绘制视图的更多信息,请参见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()
}
}
}