SwiftUI中的可扩展列表仅扩展一次,不会收缩

时间:2019-09-23 14:11:37

标签: swiftui swiftui-list

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

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

enter image description here

在明智地放置print的情况下,我可以看到在第一个问题上isExpanded被切换为true,但是不会切换回false

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

2 个答案:

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