Compare commits

..

7 Commits

Author SHA1 Message Date
fish 480404b21b 大小写切换按钮简化为 A/a 单字符
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:31:13 +08:00
fish 98a7d157f4 大小写切换按钮改为 A a 文字显示
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:30:42 +08:00
fish 6e80a5986a 修复大小写切换无实际效果问题
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:29:53 +08:00
fish 86b5cabe17 大小写切换按钮移至导航栏右侧
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:27:41 +08:00
fish b693874f69 拼写页面增加大小写切换按钮,偏好持久化缓存
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:26:29 +08:00
fish 69a1c44ded 拼写空缺支持全空,随机空缺数量
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:22:13 +08:00
fish 9124a49887 拼写空缺改为随机数量,保证至少留一个字母可见
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 21:21:18 +08:00
3 changed files with 44 additions and 8 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ enum QuizGenerator {
let chars = Array(word) let chars = Array(word)
let letterPositions = chars.indices.filter { chars[$0].isLetter } let letterPositions = chars.indices.filter { chars[$0].isLetter }
guard !letterPositions.isEmpty else { return word } guard !letterPositions.isEmpty else { return word }
let blankCount = min(letterPositions.count - 1, Int(ceil(Double(letterPositions.count) * 0.4))) let blankCount = Int.random(in: 1...letterPositions.count)
let indices = Set(letterPositions.shuffled().prefix(blankCount)) let indices = Set(letterPositions.shuffled().prefix(blankCount))
return chars.enumerated().map { indices.contains($0.offset) ? "_" : String($0.element) }.joined() return chars.enumerated().map { indices.contains($0.offset) ? "_" : String($0.element) }.joined()
} }
@@ -36,14 +36,26 @@ final class QuizViewController: UIViewController {
target: nil, target: nil,
action: nil action: nil
) )
private lazy var caseToggleItem = UIBarButtonItem(
title: "a", style: .plain, target: self, action: #selector(toggleCase)
)
private var current: QuizQuestion? { private var current: QuizQuestion? {
questions.indices.contains(currentIndex) ? questions[currentIndex] : nil questions.indices.contains(currentIndex) ? questions[currentIndex] : nil
} }
private var isUppercase: Bool
init(config: QuizConfig) { init(config: QuizConfig) {
self.config = config self.config = config
self.questions = QuizGenerator.makeQuestions(mode: config.mode, words: config.words) let uppercase = UserDefaults.standard.bool(forKey: "spelling_uppercase")
self.isUppercase = uppercase
let words = config.words.map { word in
let text = uppercase ? word.word.uppercased() : word.word.lowercased()
return Word(word: text, phonetic: word.phonetic, meaning: word.meaning,
example: word.example, exampleMeaning: word.exampleMeaning)
}
self.questions = QuizGenerator.makeQuestions(mode: config.mode, words: words)
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
} }
@@ -187,7 +199,14 @@ final class QuizViewController: UIViewController {
resultLabel.isHidden = true resultLabel.isHidden = true
resultPhoneticLabel.isHidden = true resultPhoneticLabel.isHidden = true
navigationItem.rightBarButtonItem = config.mode == .zhToEn ? nil : navSpeakerItem if config.mode == .spelling {
updateCaseButton()
navigationItem.rightBarButtonItems = [navSpeakerItem, caseToggleItem]
} else if config.mode == .zhToEn {
navigationItem.rightBarButtonItems = nil
} else {
navigationItem.rightBarButtonItem = navSpeakerItem
}
let isSpelling = config.mode == .spelling let isSpelling = config.mode == .spelling
let isDictation = config.mode == .dictation let isDictation = config.mode == .dictation
@@ -331,7 +350,7 @@ final class QuizViewController: UIViewController {
private func submitSpelling(_ typed: String, for question: QuizQuestion) { private func submitSpelling(_ typed: String, for question: QuizQuestion) {
guard selectedOption == nil else { return } guard selectedOption == nil else { return }
selectedOption = typed selectedOption = typed
let correct = typed == question.answer let correct = typed.lowercased() == question.answer.lowercased()
if !correct { if !correct {
wrongWords.append(question.word) wrongWords.append(question.word)
} }
@@ -344,7 +363,7 @@ final class QuizViewController: UIViewController {
private func submitDictation(_ typed: String, for question: QuizQuestion) { private func submitDictation(_ typed: String, for question: QuizQuestion) {
guard selectedOption == nil else { return } guard selectedOption == nil else { return }
selectedOption = typed selectedOption = typed
let correct = typed == question.answer let correct = typed.lowercased() == question.answer.lowercased()
if !correct { if !correct {
wrongWords.append(question.word) wrongWords.append(question.word)
} }
@@ -390,6 +409,18 @@ final class QuizViewController: UIViewController {
} }
} }
@objc private func toggleCase() {
isUppercase.toggle()
UserDefaults.standard.set(isUppercase, forKey: "spelling_uppercase")
updateCaseButton()
restart()
}
private func updateCaseButton() {
caseToggleItem.title = isUppercase ? "A" : "a"
caseToggleItem.tintColor = .label
}
@objc private func speakCurrentWord() { @objc private func speakCurrentWord() {
guard let question = current else { return } guard let question = current else { return }
speech.speak(question.word.word) speech.speak(question.word.word)
@@ -465,7 +496,12 @@ final class QuizViewController: UIViewController {
countdownTimer?.invalidate() countdownTimer?.invalidate()
countdownLabel.isHidden = true countdownLabel.isHidden = true
view.subviews.compactMap { $0 as? QuizResultView }.forEach { $0.removeFromSuperview() } view.subviews.compactMap { $0 as? QuizResultView }.forEach { $0.removeFromSuperview() }
questions = QuizGenerator.makeQuestions(mode: config.mode, words: config.words) let words = config.words.map { word in
let text = isUppercase ? word.word.uppercased() : word.word.lowercased()
return Word(word: text, phonetic: word.phonetic, meaning: word.meaning,
example: word.example, exampleMeaning: word.exampleMeaning)
}
questions = QuizGenerator.makeQuestions(mode: config.mode, words: words)
currentIndex = 0 currentIndex = 0
wrongWords = [] wrongWords = []
startDate = .now startDate = .now
@@ -141,7 +141,7 @@ final class SpellingInputView: UIView, UITextFieldDelegate {
let box = makeBox(fontSize: fontSize) let box = makeBox(fontSize: fontSize)
box.tag = index box.tag = index
if ch != "_", let label = box.viewWithTag(100) as? UILabel { if ch != "_", let label = box.viewWithTag(100) as? UILabel {
label.text = ch.uppercased() label.text = ch
label.isHidden = false label.isHidden = false
} }
box.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(boxTapped(_:)))) box.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(boxTapped(_:))))
@@ -199,7 +199,7 @@ final class SpellingInputView: UIView, UITextFieldDelegate {
} }
let label = box.viewWithTag(100) as? UILabel let label = box.viewWithTag(100) as? UILabel
let filled = filledBlanks[blankIndex] let filled = filledBlanks[blankIndex]
label?.text = filled.uppercased() label?.text = filled
label?.isHidden = filled.isEmpty label?.isHidden = filled.isEmpty
let isCurrent = inputEnabled && blankIndex == cursorIndex let isCurrent = inputEnabled && blankIndex == cursorIndex