673e35aec2
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
96 lines
3.4 KiB
Swift
96 lines
3.4 KiB
Swift
import UIKit
|
|
|
|
final class HistoryViewController: UITableViewController {
|
|
private let sections: [(title: String, items: [LearnedRecord])]
|
|
private var speechObserver: NSObjectProtocol?
|
|
|
|
init() {
|
|
sections = LearnedStore.shared.sectioned
|
|
super.init(style: .plain)
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) { fatalError() }
|
|
|
|
deinit {
|
|
if let speechObserver {
|
|
NotificationCenter.default.removeObserver(speechObserver)
|
|
}
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
title = "学习记录"
|
|
navigationItem.largeTitleDisplayMode = .never
|
|
tableView.register(WordCardCell.self, forCellReuseIdentifier: "cell")
|
|
tableView.separatorStyle = .none
|
|
tableView.backgroundColor = .systemBackground
|
|
tableView.contentInset.top = 8
|
|
updateEmptyState()
|
|
|
|
speechObserver = NotificationCenter.default.addObserver(
|
|
forName: SpeechService.speakingDidChangeNotification, object: nil, queue: .main
|
|
) { [weak self] _ in
|
|
self?.updateVisibleSpeakers()
|
|
}
|
|
}
|
|
|
|
private func updateEmptyState() {
|
|
if sections.isEmpty {
|
|
let label = UILabel()
|
|
label.text = "暂无学习记录"
|
|
label.textAlignment = .center
|
|
label.textColor = .secondaryLabel
|
|
label.font = .preferredFont(forTextStyle: .body)
|
|
let bg = UIView()
|
|
bg.addSubview(label)
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
NSLayoutConstraint.activate([
|
|
label.centerXAnchor.constraint(equalTo: bg.centerXAnchor),
|
|
label.centerYAnchor.constraint(equalTo: bg.centerYAnchor)
|
|
])
|
|
tableView.backgroundView = bg
|
|
}
|
|
}
|
|
|
|
private func updateVisibleSpeakers() {
|
|
for cell in tableView.visibleCells.compactMap({ $0 as? WordCardCell }) {
|
|
cell.updateSpeakerState()
|
|
}
|
|
}
|
|
|
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
sections.count
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
sections[section].title
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
sections[section].items.count
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! WordCardCell
|
|
let record = sections[indexPath.section].items[indexPath.row]
|
|
cell.configure(with: record.word)
|
|
cell.onSpeak = { word in
|
|
SpeechService.shared.speak(word.word)
|
|
}
|
|
cell.onLearn = { [weak self] word in
|
|
guard let self else { return }
|
|
let config = QuizConfig(mode: .spelling, words: [word], isStudyMode: true)
|
|
navigationController?.pushViewController(QuizViewController(config: config), animated: true)
|
|
}
|
|
return cell
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
Haptics.selection()
|
|
let record = sections[indexPath.section].items[indexPath.row]
|
|
let detail = WordDetailViewController(word: record.word, categoryName: "")
|
|
navigationController?.pushViewController(detail, animated: true)
|
|
}
|
|
}
|