feat: replace Leaderboard with Highscore

This commit is contained in:
2025-08-11 19:08:28 +02:00
parent a7907d59b9
commit de8547749c
4 changed files with 48 additions and 114 deletions

View File

@@ -91,4 +91,26 @@ class ViewModel: ObservableObject {
return 0 return 0
} }
} }
func highscore(for category: String) -> Int {
let dict = UserDefaults.standard.dictionary(forKey: "categoryHighscores") as? [String: Int] ?? [:]
return dict[category] ?? 0
}
@discardableResult
func updateHighscoreIfNeeded() -> Bool {
guard let category = selectedCategory else { return false }
var dict = UserDefaults.standard.dictionary(forKey: "categoryHighscores") as? [String: Int] ?? [:]
let oldScore = dict[category] ?? 0
if score > oldScore {
dict[category] = score
UserDefaults.standard.set(dict, forKey: "categoryHighscores")
return true
}
return false
}
func allHighscores() -> [String: Int] {
UserDefaults.standard.dictionary(forKey: "categoryHighscores") as? [String: Int] ?? [:]
}
} }

View File

@@ -10,11 +10,7 @@ import SwiftUI
struct CategorySelectionView: View { struct CategorySelectionView: View {
@StateObject private var viewModel = ViewModel() @StateObject private var viewModel = ViewModel()
@AppStorage("globalScore") var globalScore: Int = 0 @AppStorage("globalScore") var globalScore: Int = 0
@State private var boardData: [[String: Any]] = []
private func loadBoardData() {
boardData = UserDefaults.standard.array(forKey: "leaderboard") as? [[String: Any]] ?? []
}
var body: some View { var body: some View {
NavigationStack { NavigationStack {
@@ -78,9 +74,14 @@ struct CategorySelectionView: View {
.foregroundColor(.blue) .foregroundColor(.blue)
.font(.title2) .font(.title2)
Text(category) VStack(alignment: .leading, spacing: 4) {
.font(.headline) Text(category)
.foregroundColor(.primary) .font(.headline)
.foregroundColor(.primary)
Text("Highscore: \(viewModel.highscore(for: category))")
.font(.caption)
.foregroundColor(.secondary)
}
Spacer() Spacer()
@@ -103,58 +104,11 @@ struct CategorySelectionView: View {
} }
.padding(.horizontal) .padding(.horizontal)
// Scoreboard
Text("Leaderboard")
.font(.title2)
.fontWeight(.semibold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
if !boardData.isEmpty {
VStack(spacing: 8) {
ForEach(Array(boardData.prefix(5).enumerated()), id: \.offset) { index, scoreData in
HStack {
Text("\(index + 1).")
.font(.headline)
.foregroundColor(index < 3 ? .orange : .primary)
.frame(width: 25, alignment: .leading)
Text(scoreData["name"] as? String ?? "Unbekannt")
.font(.body)
.foregroundColor(.primary)
Spacer()
Text("\(scoreData["score"] as? Int ?? 0) Punkte")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.blue)
}
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.systemGray6))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color(.systemGray4), lineWidth: 1)
)
)
}
}
.padding(.horizontal)
} else {
Text("Keine Leadboard Daten...")
}
Spacer() Spacer()
} }
.background(Color.white) .background(Color.white)
.navigationBarHidden(true) .navigationBarHidden(true)
.navigationTitle("Quiz-Auswahl") .navigationTitle("Quiz-Auswahl")
.onAppear {
loadBoardData()
}
} }
} }
} }

View File

@@ -10,10 +10,9 @@ import SwiftUI
struct QuizFinishedView: View { struct QuizFinishedView: View {
let score: Int let score: Int
let total: Int let total: Int
@Binding var playerName: String let isNewHighscore: Bool
let onPlayAgain: () -> Void let onPlayAgain: () -> Void
let onBackToCategories: () -> Void let onBackToCategories: () -> Void
let onSaveScore: () -> Void
var body: some View { var body: some View {
VStack(spacing: 20) { VStack(spacing: 20) {
@@ -23,6 +22,12 @@ struct QuizFinishedView: View {
Text("Dein Ergebnis: \(score) von \(total) Punkten") Text("Dein Ergebnis: \(score) von \(total) Punkten")
.font(.headline) .font(.headline)
if isNewHighscore {
Text("🔥 Neuer Highscore!")
.font(.headline)
.foregroundColor(.orange)
}
Button("Nochmal spielen", action: onPlayAgain) Button("Nochmal spielen", action: onPlayAgain)
.padding() .padding()
.background(Color.blue) .background(Color.blue)
@@ -34,17 +39,7 @@ struct QuizFinishedView: View {
.background(Color.gray) .background(Color.gray)
.foregroundColor(.white) .foregroundColor(.white)
.cornerRadius(10) .cornerRadius(10)
TextField("Dein Name:", text: $playerName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.horizontal)
Button("Score speichern", action: onSaveScore)
.padding()
.background(playerName.isEmpty ? Color.gray : Color.green)
.foregroundColor(.white)
.cornerRadius(10)
.disabled(playerName.isEmpty)
} }
} }
} }

View File

@@ -19,8 +19,7 @@ struct QuizView: View {
@State private var selectedAnswerIndex: Int? = nil @State private var selectedAnswerIndex: Int? = nil
@State private var isAnswered = false @State private var isAnswered = false
@State private var showExitAlert = false @State private var showExitAlert = false
@State private var playerName: String = "" @State private var isNewHighscore = false
@State private var scoreIsSaved = false
@State private var estimationValue: Double = 0 @State private var estimationValue: Double = 0
@State private var estimationSubmitted: Bool = false @State private var estimationSubmitted: Bool = false
@@ -32,17 +31,15 @@ struct QuizView: View {
QuizFinishedView( QuizFinishedView(
score: viewModel.score, score: viewModel.score,
total: viewModel.questions.count, total: viewModel.questions.count,
playerName: $playerName, isNewHighscore: isNewHighscore,
onPlayAgain: { restartQuiz() }, onPlayAgain: { restartQuiz() },
onBackToCategories: { dismiss() }, onBackToCategories: { dismiss() }
onSaveScore: {
saveScore()
dismiss()
}
) )
.onAppear { .onAppear {
AudioServicesPlaySystemSound(1322) AudioServicesPlaySystemSound(1322)
score += viewModel.score score += viewModel.score
// Highscore prüfen/setzen
isNewHighscore = viewModel.updateHighscoreIfNeeded()
} }
.onDisappear { .onDisappear {
viewModel.isQuizFinished = false viewModel.isQuizFinished = false
@@ -131,10 +128,7 @@ struct QuizView: View {
} }
} }
.onAppear { .onAppear {
// Fragen für die Kategorie viewModel.loadQuestions(for: category)
if viewModel.selectedCategory != category {
viewModel.loadQuestions(for: category)
}
} }
.navigationBarBackButtonHidden(!viewModel.isQuizFinished) .navigationBarBackButtonHidden(!viewModel.isQuizFinished)
.toolbar { .toolbar {
@@ -206,37 +200,6 @@ struct QuizView: View {
} }
} }
} }
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 { #Preview {