总的来说,我对快速编程相当陌生,但我正在尝试构建一个有效的小测验视图,该视图接受JSON文件中的问答问题,并使用自定义的“应答”视图将它们以LazyVGrid模式显示在屏幕上。
这是我的自定义答案视图(很抱歉,测试在所有这些名称上,我有重复的文件来尝试不同的事情而不干扰我的原始文件!):
struct TestAnswerTest: View {
@State var answer: String
@State private var selected = false
var correctAnswers: [String]
var body: some View {
Text(answer)
.font(.title)
.bold()
.frame(width: 165, height: 140)
.contentShape(RoundedRectangle(cornerRadius: 25))
.onTapGesture {
self.selected.toggle()
}
}
}我的问题结构:
struct Question2: Codable, Identifiable {
let id: Int
let category: String
let question: String
let answers: [String]
let correctAnswers: [String]
let options: Int
}我的json数据样本供参考:
{
"id": 1,
"category": "staff",
"question": "What are the 2 common names for the lines and spaces on sheet music?\n(Select 2 answers)",
"answers": ["Staff", "Stave", "String", "Stem", "Stake", "Store"],
"correctAnswers": ["Staff", "Stave"],
"options": 6
}这是我的小测验显示视图:
struct StaffRecap2: View {
@State private var questionNumber = 0
private let twoColumnGrid = [GridItem(.flexible()), GridItem(.flexible())]
let questions:[Question2] = Bundle.main.decode("questions.json")
var body: some View {
let staffQuestions = questions.filter { $0.category.elementsEqual("staff")}
NavigationView {
VStack{
Text(staffQuestions[questionNumber].question)
LazyVGrid(columns: twoColumnGrid) {
ForEach(0..<staffQuestions[questionNumber].options, id:\.self) { number in
TestAnswerTest(answer: staffQuestions[questionNumber].answers[number],
correctAnswers: staffQuestions[questionNumber].correctAnswers)
}
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button {
questionNumber += 1
print(staffQuestions[questionNumber])
} label: {
Text("Next Question")
}
.disabled(questionNumber == staffQuestions.count)
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Gradient(colors: [.teal, .blue]))
.navigationTitle("Staff Recap")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.orange, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
}
}
}我的问题是,如果我手动更改"questionNumber“属性和模拟器中的代码构建,并预览所有正确的详细信息,一切似乎都正常,但是如果我使用按钮转到下一个问题,它就会正确地更改问题,但不会更新新的答案选项的任何答案视图。我真的不明白为什么我在应答视图中使用correctAnswers修饰符打印onTapGesture数组,它也为这个问题打印正确的数组,并在我按下按钮时正确地更改。
我有点困惑,我相信这可能是很简单的事情,我错过了,但我陷入了僵局,希望能为我做错的事情找到正确方向的帮助或指点。
提前感谢!
发布于 2022-10-01 06:25:27
下面是我在评论中提到的方法的一个非常基本的例子,即使用ObservableObject视图模型,在这里执行所有的数据处理。注意,我更新了Question模型和相关的json数据。
import Foundation
import SwiftUI
class QuestionModel: ObservableObject {
@Published var questions:[Question] = []
init() {
if let loaded: [Question] = Bundle.main.decode("questions.json") {
questions = loaded
}
}
func addAnswer(to n: Int, answer: Answer) {
questions[n].answers.append(answer)
}
func removeAnswer(from n: Int, answer: Answer) {
questions[n].answers.removeAll(where: {$0.id == answer.id})
}
func isCorrectAnswer(_ n: Int) -> Bool {
let arr1 = questions[n].correctAnswers.map{$0.id}
let arr2 = questions[n].answers.map{$0.id}
let theSet = Set(arr2).subtracting(arr1)
return theSet.count == 0 && questions[n].correctAnswers.count == questions[n].answers.count
}
}
struct Question: Codable, Identifiable {
let id: Int
var category: String
let question: String
var answers: [Answer] // <-- the answers from the user
var possibles: [Answer] // <-- the possible choices of answers
var correctAnswers: [Answer] // <-- just the correct answers
}
struct Answer: Codable, Identifiable, Hashable {
let id: Int
var answer: String
var isCorrect: Bool
var answered: Bool
}
struct ContentView: View {
@StateObject var model = QuestionModel()
@State private var questionNumber = 0
@State private var showAnswer = false
private let twoColumnGrid = [GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
NavigationView {
ScrollView {
Text(model.questions[questionNumber].question)
LazyVGrid(columns: twoColumnGrid) {
ForEach($model.questions[questionNumber].possibles) { $answer in
AnswerView(questionNumber: $questionNumber, answer: $answer)
}
}
NavigationLink("", destination: AnswerInfo(questionNumber: questionNumber), isActive: $showAnswer)
}
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button {
if questionNumber + 1 < model.questions.count {
questionNumber += 1
}
} label: {
Text("Next Question").foregroundColor(.black)
}
Button {
showAnswer = true
} label: {
Text("Check answer").foregroundColor(.black)
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Gradient(colors: [.teal, .blue]))
}
.environmentObject(model)
.navigationViewStyle(.stack)
.navigationTitle("Staff Recap")
.navigationBarTitleDisplayMode(.inline)
}
}
struct AnswerInfo: View {
@EnvironmentObject var model: QuestionModel
@State var questionNumber: Int
var body: some View {
VStack {
if model.isCorrectAnswer(questionNumber) {
Text("Correct answer").font(.title)
} else {
Text("NOT correct answer").font(.title)
}
ForEach(model.questions[questionNumber].answers) { answer in
Text(answer.answer)
}
}
}
}
struct AnswerView: View {
@EnvironmentObject var model: QuestionModel
@Binding var questionNumber: Int
@Binding var answer: Answer
var body: some View {
Button {
answer.answered.toggle()
if answer.answered {
model.addAnswer(to: questionNumber, answer: answer)
} else {
model.removeAnswer(from: questionNumber, answer: answer)
}
} label: {
Text(answer.answer)
.font(.title)
.bold()
.frame(width: 165, height: 140)
.contentShape(RoundedRectangle(cornerRadius: 25))
.foregroundColor(answer.answered ? Color.red : Color.green)
}
.buttonStyle(.bordered)
}
}
extension Bundle {
func decode<T: Decodable>(_ file: String) -> T? {
if let url = self.url(forResource: file, withExtension: nil) {
do {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(T.self, from: data)
} catch {
print(error)
}
}
return nil
}
}questions.json文件中的json数据
[
{
"id": 1,
"category": "staff",
"question": "What are the 2 common names for the lines and spaces on sheet music?\n(Select 2 answers)",
"answers": [],
"possibles": [
{"id": 1, "answer": "Staff", "isCorrect": false, "answered": false},
{"id": 2, "answer": "Stave", "isCorrect": false, "answered": false},
{"id": 3, "answer": "String", "isCorrect": false, "answered": false},
{"id": 4, "answer": "Stem", "isCorrect": false, "answered": false},
{"id": 5, "answer": "Stake", "isCorrect": false, "answered": false},
{"id": 6, "answer": "Store", "isCorrect": false, "answered": false},
],
"correctAnswers": [
{"id": 1, "answer": "Staff", "isCorrect": true, "answered": false},
{"id": 2, "answer": "Stave", "isCorrect": true, "answered": false}
]
},
{
"id": 2,
"category": "cats",
"question": "What is this cat?",
"answers": [],
"possibles": [
{"id": 1, "answer": "Cat-1", "isCorrect": false, "answered": false},
{"id": 2, "answer": "Cat-2", "isCorrect": false, "answered": false},
{"id": 3, "answer": "Cat-3", "isCorrect": false, "answered": false},
{"id": 4, "answer": "Cat-4", "isCorrect": false, "answered": false}
],
"correctAnswers": [
{"id": 2, "answer": "Cat-2", "isCorrect": true, "answered": false}
]
}
]https://stackoverflow.com/questions/73902175
复制相似问题