3

我正在尝试(但失败了!)在 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)
                        }
                    }
                }
            }
        }
    }
}

成功点击任何问题会将答案展开到视图中,但再次点击同一标题不会缩小答案,点击第二个问题也不会扩大这些答案。

在此处输入图像描述

通过一些明智地放置prints ,我可以看到在点击的第一个问题上isExpanded切换到true,但随后不会切换回false

有人可以解释我做错了什么吗?

4

2 回答 2

2

问题在于你的@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@PublishedViewModel

于 2019-09-23T15:56:58.357 回答
0

通过将视图拆分为较小的自包含视图,可以轻松实现这一点。

模型不需要知道答案是否显示在屏幕上。因此,无需查看数组即可知道视图是否已展开。

此外,当其中一个部分展开时,不需要重新绘制整个列表。有关何时重绘视图的更多信息,请参阅这篇文章

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()
        }
    }
}

于 2020-04-08T16:46:06.983 回答