Files
QuizApp/QuizApp/Views/QuizView.swift

245 lines
8.7 KiB
Swift

//
// ContentView.swift
// QuizApp
//
// Created by Paul on 02.08.25.
//
import SwiftUI
import AudioToolbox
struct QuizView: View {
@ObservedObject var viewModel = ViewModel()
@AppStorage("globalScore") var score: Int = 0
@Environment(\.dismiss) var dismiss
let category: String
@State private var selectedAnswerIndex: Int? = nil
@State private var isAnswered = false
@State private var showExitAlert = false
@State private var playerName: String = ""
@State private var scoreIsSaved = false
@State private var estimationValue: Double = 0
@State private var estimationSubmitted: Bool = false
@State private var estimationPoints: Int = 0
var body: some View {
VStack {
if viewModel.isQuizFinished {
QuizFinishedView(
score: viewModel.score,
total: viewModel.questions.count,
playerName: $playerName,
onPlayAgain: { restartQuiz() },
onBackToCategories: { dismiss() },
onSaveScore: {
saveScore()
dismiss()
}
)
.onAppear {
AudioServicesPlaySystemSound(1322)
score += viewModel.score
}
.onDisappear {
viewModel.isQuizFinished = false
}
} else if let frage = viewModel.currentQuestion {
QuizHeader(
score: viewModel.score,
currentIndex: viewModel.currentQuestionIndex,
total: viewModel.questions.count,
colorForStep: { idx in colorForAnswer(at: idx) }
)
Text(frage.question)
.font(.title)
.multilineTextAlignment(.center)
.padding()
if frage.isEstimation,
let minV = frage.minValue,
let maxV = frage.maxValue,
let correctV = frage.correctValue {
// Estimation
EstimationQuestionView(
minValue: minV,
maxValue: maxV,
correctValue: correctV,
unit: frage.unit ?? "",
viewModel: viewModel,
value: $estimationValue,
submitted: $estimationSubmitted,
gainedPoints: $estimationPoints
) { gained in
viewModel.score += gained
viewModel.answeredCount += 1
viewModel.selectedAnswers[viewModel.currentQuestionIndex] = 0
isAnswered = true
}
if estimationSubmitted {
Button {
viewModel.loadNextQuestion()
// Reset für nächste Estimation
estimationSubmitted = false
isAnswered = false
selectedAnswerIndex = nil
estimationPoints = 0
} label: {
Text("Nächste Frage")
.font(.headline)
.padding(.vertical, 10)
.padding(.horizontal, 30)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding(.top, 8)
}
} else {
// Multiple-Choice
MultipleChoiceQuestionView(
answers: frage.answers,
correctIndex: frage.correctAnswer,
selectedIndex: $selectedAnswerIndex,
isAnswered: $isAnswered,
onAnswer: { index in
print("Selected:", index)
print("Correct:", frage.correctAnswer)
viewModel.incrementScore(selectedIndex: index)
viewModel.answeredCount += 1
viewModel.selectedAnswers[viewModel.currentQuestionIndex] = index
},
buttonColor: { idx, correct in
buttonColor(for: idx, correctIndex: correct)
},
onNext: {
viewModel.loadNextQuestion()
selectedAnswerIndex = nil
isAnswered = false
}
)
}
} else {
Text("Fragen werden geladen...")
}
}
.onAppear {
// Fragen für die Kategorie
if viewModel.selectedCategory != category {
viewModel.loadQuestions(for: category)
}
}
.navigationBarBackButtonHidden(!viewModel.isQuizFinished)
.toolbar {
if !viewModel.isQuizFinished {
ToolbarItem(placement: .navigationBarLeading) {
Button("Abbrechen") {
showExitAlert = true
}
.foregroundColor(.red)
}
}
}
.alert("Quiz verlassen?", isPresented: $showExitAlert) {
Button("Quiz verlassen", role: .destructive) {
dismiss()
}
Button("Weiter spielen", role: .cancel) {
}
} message: {
Text("Deine im Quiz gesammelten Punkte werden nicht gespeichert.")
}
}
private func buttonColor(for index: Int, correctIndex: Int) -> Color {
guard isAnswered else { return .blue }
if index == correctIndex {
return .green
} else if index == selectedAnswerIndex {
return .red
} else {
return .gray
}
}
private func restartQuiz() {
viewModel.currentQuestionIndex = 0
viewModel.score = 0
viewModel.isQuizFinished = false
selectedAnswerIndex = nil
isAnswered = false
viewModel.answeredCount = 0
viewModel.selectedAnswers = Array(repeating: nil, count: viewModel.questions.count)
}
private func colorForAnswer(at index: Int) -> Color {
if index == viewModel.currentQuestionIndex,
viewModel.selectedAnswers[index] == nil {
return Color.blue // aktuelle Frage
}
guard index < viewModel.selectedAnswers.count,
index < viewModel.questions.count else {
return Color.gray.opacity(0.3) // unbeantwortet
}
// Prüfen ob die Frage beantwortet wurde
let question = viewModel.questions[index]
if question.isEstimation {
// Bei Estimation-Fragen: Grün wenn beantwortet
return viewModel.selectedAnswers[index] != nil ? .green : Color.gray.opacity(0.3)
} else {
// Bei Multiple-Choice: Grün wenn richtig, rot wenn falsch
if let selected = viewModel.selectedAnswers[index] {
let correct = question.correctAnswer
return selected == correct ? .green : .red
} else {
return Color.gray.opacity(0.3) // unbeantwortet
}
}
}
private func saveScore() {
// Bestehende Scores laden
var savedScores = UserDefaults.standard.array(forKey: "leaderboard") as? [[String: Any]] ?? []
// Neuen Score hinzufügen
let newScore: [String: Any] = [
"name": playerName,
"score": viewModel.score,
"date": Date()
]
savedScores.append(newScore)
// Nach Score sortieren
savedScores.sort { score1, score2 in
let score1Value = score1["score"] as? Int ?? 0
let score2Value = score2["score"] as? Int ?? 0
return score1Value > score2Value
}
// Nur die besten 10 Scores behalten
if savedScores.count > 5 {
savedScores = Array(savedScores.prefix(10))
}
UserDefaults.standard.set(savedScores, forKey: "leaderboard")
scoreIsSaved = true
print("Score gespeichert: \(playerName) - \(viewModel.score) Punkte")
}
}
#Preview {
CategorySelectionView()
}