diff --git a/LearnEnglish/LearnEnglish/Models/Quiz.swift b/LearnEnglish/LearnEnglish/Models/Quiz.swift index 6c99eef..332152e 100644 --- a/LearnEnglish/LearnEnglish/Models/Quiz.swift +++ b/LearnEnglish/LearnEnglish/Models/Quiz.swift @@ -3,6 +3,7 @@ import Foundation enum QuizMode: String, Hashable { case enToZh = "英选汉" case zhToEn = "汉选英" + case spelling = "拼写" } struct QuizSetupRequest: Hashable { @@ -21,6 +22,7 @@ struct QuizQuestion: Identifiable { let prompt: String let options: [String] let answer: String + let blankedWord: String } enum QuizGenerator { @@ -32,16 +34,34 @@ enum QuizGenerator { word: word, prompt: promptText(word, mode), options: options, - answer: optionText(word, mode) + answer: optionText(word, mode), + blankedWord: mode == .spelling ? blankWord(word.word) : "" ) } } private static func promptText(_ word: Word, _ mode: QuizMode) -> String { - mode == .enToZh ? word.word : word.meaning + switch mode { + case .enToZh: return word.word + case .zhToEn: return word.meaning + case .spelling: return word.meaning + } } private static func optionText(_ word: Word, _ mode: QuizMode) -> String { - mode == .enToZh ? word.meaning : word.word + switch mode { + case .enToZh: return word.meaning + case .zhToEn: return word.word + case .spelling: return word.word + } + } + + private static func blankWord(_ word: String) -> String { + let chars = Array(word) + let count = chars.count + guard count > 2 else { return String(repeating: "_", count: count) } + let blanks = max(1, Int(Double(count) * 0.4)) + let indices = Set(Array(0.. some View { + VStack(spacing: 12) { + ForEach(question.options, id: \.self) { option in + Button { + select(option, for: question) + } label: { + VStack(spacing: 4) { + Text(option) + .font(.headline) + if config.mode == .zhToEn, let w = config.words.first(where: { $0.word == option }) { + Text(w.phonetic) + .font(.caption) + .foregroundStyle(.secondary) + } + } + .frame(maxWidth: .infinity) + .padding(.vertical, 10) + } + .buttonStyle(.bordered) + .tint(optionColor(option, for: question)) + .disabled(selectedOption != nil) + } + } + } + + private func spellingView(_ question: QuizQuestion) -> some View { + VStack(spacing: 16) { + Text(question.blankedWord) + .font(.system(size: 44, weight: .bold, design: .monospaced)) + .tracking(6) + Text("\(question.word.word.count) 个字母") + .font(.subheadline) + .foregroundStyle(.secondary) + HStack(spacing: 12) { + TextField("输入完整单词", text: $userInput) + .textFieldStyle(.roundedBorder) + .autocapitalization(.none) + .disableAutocorrection(true) + .disabled(selectedOption != nil) + Button("确认") { + guard !userInput.trimmingCharacters(in: .whitespaces).isEmpty else { return } + selectedOption = userInput.trimmingCharacters(in: .whitespaces).lowercased() + if selectedOption != question.answer { + wrongWords.append(question.word) + } + } + .buttonStyle(.borderedProminent) + .disabled(userInput.trimmingCharacters(in: .whitespaces).isEmpty || selectedOption != nil) + } + } + } + private func optionColor(_ option: String, for question: QuizQuestion) -> Color { guard let selectedOption else { return .accentColor } if option == question.answer { return .green } @@ -131,6 +176,7 @@ struct QuizView: View { if currentIndex + 1 < questions.count { currentIndex += 1 selectedOption = nil + userInput = "" } else { finished = true } @@ -140,6 +186,7 @@ struct QuizView: View { questions = QuizGenerator.makeQuestions(mode: config.mode, words: config.words) currentIndex = 0 selectedOption = nil + userInput = "" wrongWords = [] finished = false startDate = .now diff --git a/LearnEnglish/LearnEnglish/Views/WordListView.swift b/LearnEnglish/LearnEnglish/Views/WordListView.swift index 192943f..8827511 100644 --- a/LearnEnglish/LearnEnglish/Views/WordListView.swift +++ b/LearnEnglish/LearnEnglish/Views/WordListView.swift @@ -25,6 +25,7 @@ struct WordListView: View { Menu { Button("英选汉") { startQuiz(.enToZh) } Button("汉选英") { startQuiz(.zhToEn) } + Button("拼写") { startQuiz(.spelling) } } label: { Text("测验") }