单词列表页导航栏新增刷新按钮,支持随机重排单词顺序

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 22:56:12 +08:00
parent 0a133593e5
commit ef0acbecc3
2 changed files with 45 additions and 20 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ struct WordCategory: Codable, Identifiable, Hashable {
struct Subcategory: Codable, Hashable { struct Subcategory: Codable, Hashable {
let name: String let name: String
let words: [Word] var words: [Word]
} }
struct Word: Codable, Identifiable, Hashable { struct Word: Codable, Identifiable, Hashable {
@@ -3,14 +3,22 @@ import UIKit
final class WordListViewController: UIViewController { final class WordListViewController: UIViewController {
private let category: WordCategory private let category: WordCategory
private let colorIndex: Int private let colorIndex: Int
private var words: [Word]
private var subcategories: [Subcategory]?
private let tableView = UITableView(frame: .zero, style: .plain) private let tableView = UITableView(frame: .zero, style: .plain)
private let ctaBackground = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial)) private let ctaBackground = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
private let quizButton = UIButton(type: .system) private let quizButton = UIButton(type: .system)
private var speechObserver: NSObjectProtocol? private var speechObserver: NSObjectProtocol?
private var displaySubcategories: [Subcategory]? {
subcategories.flatMap { $0.isEmpty ? nil : $0 }
}
init(category: WordCategory, colorIndex: Int) { init(category: WordCategory, colorIndex: Int) {
self.category = category self.category = category
self.colorIndex = colorIndex self.colorIndex = colorIndex
self.words = category.words
self.subcategories = category.subcategories
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
} }
@@ -27,6 +35,12 @@ final class WordListViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
title = category.name title = category.name
navigationItem.largeTitleDisplayMode = .never navigationItem.largeTitleDisplayMode = .never
navigationItem.rightBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "arrow.triangle.2.circlepath"),
style: .plain,
target: self,
action: #selector(shuffleWords)
)
view.backgroundColor = .systemBackground view.backgroundColor = .systemBackground
tableView.dataSource = self tableView.dataSource = self
@@ -119,16 +133,31 @@ final class WordListViewController: UIViewController {
private func startQuiz() { private func startQuiz() {
Haptics.selection() Haptics.selection()
let words: [Word] let quizWords: [Word]
if category.id == "numbers" { if category.id == "numbers" {
words = NumberWords.randomWords(count: 50) quizWords = NumberWords.randomWords(count: 50)
} else { } else {
words = category.words quizWords = words
} }
let request = QuizSetupRequest(words: words) let request = QuizSetupRequest(words: quizWords)
navigationController?.pushViewController(QuizSetupViewController(request: request), animated: true) navigationController?.pushViewController(QuizSetupViewController(request: request), animated: true)
} }
@objc private func shuffleWords() {
Haptics.selection()
if var subs = subcategories {
subs = subs.map { sub in
var s = sub
s.words = sub.words.shuffled()
return s
}
subcategories = subs
} else {
words.shuffle()
}
tableView.reloadData()
}
private func updateVisibleSpeakers() { private func updateVisibleSpeakers() {
for cell in tableView.visibleCells.compactMap({ $0 as? WordCardCell }) { for cell in tableView.visibleCells.compactMap({ $0 as? WordCardCell }) {
cell.updateSpeakerState() cell.updateSpeakerState()
@@ -137,32 +166,28 @@ final class WordListViewController: UIViewController {
} }
extension WordListViewController: UITableViewDataSource, UITableViewDelegate { extension WordListViewController: UITableViewDataSource, UITableViewDelegate {
private var sections: [Subcategory]? {
category.subcategories.flatMap { $0.isEmpty ? nil : $0 }
}
func numberOfSections(in tableView: UITableView) -> Int { func numberOfSections(in tableView: UITableView) -> Int {
sections?.count ?? 1 displaySubcategories?.count ?? 1
} }
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
sections?[section].name displaySubcategories?[section].name
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections { if let subs = displaySubcategories {
return sections[section].words.count return subs[section].words.count
} }
return category.words.count return words.count
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! WordCardCell let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! WordCardCell
let word: Word let word: Word
if let sections { if let subs = displaySubcategories {
word = sections[indexPath.section].words[indexPath.row] word = subs[indexPath.section].words[indexPath.row]
} else { } else {
word = category.words[indexPath.row] word = words[indexPath.row]
} }
cell.configure(with: word) cell.configure(with: word)
cell.onSpeak = { word in cell.onSpeak = { word in
@@ -179,10 +204,10 @@ extension WordListViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
Haptics.selection() Haptics.selection()
let word: Word let word: Word
if let sections { if let subs = displaySubcategories {
word = sections[indexPath.section].words[indexPath.row] word = subs[indexPath.section].words[indexPath.row]
} else { } else {
word = category.words[indexPath.row] word = words[indexPath.row]
} }
let detail = WordDetailViewController(word: word, categoryName: category.name) let detail = WordDetailViewController(word: word, categoryName: category.name)
navigationController?.pushViewController(detail, animated: true) navigationController?.pushViewController(detail, animated: true)