245 lines
8.7 KiB
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()
|
|
}
|