修复阅读器资源、朗读与持久化稳定性
阅读器在多会话朗读、加密 EPUB 资源、并发打开同一本书和多 UserDefaults 容器等场景下,存在旧异步结果覆盖新状态、锁屏控制串会话、大型明文资源被误拒绝、解压半成品被复用及标注跨容器混用的风险;同时高亮、书签、搜索和设置面板的空状态与自动化可访问性入口不完整。\n\n本次为朗读会话引入代次和当前 utterance 校验,远程控制改为仅响应当前会话并按自身 token 清理;加密资源 provider 先判定是否实际返回解密数据,明文大资源继续流式读取;解压缓存串行化以避免并发复用未完成目录。书签与高亮迁移至受保护文件,按 UserDefaults 容器隔离命名空间,保留标准容器旧文件兼容并确保新副本成功落盘后才删除历史数据。\n\n同步调整缓存淘汰、FoundationModels 弱链接、搜索/标注/设置面板交互与 UI 测试,并更新 Pod 生成配置及 API、风险文档。已执行 git diff --check 和 ReadViewDemo Debug Simulator 构建,结果为 BUILD SUCCEEDED。
This commit is contained in:
@@ -16,6 +16,10 @@ final class RDEPUBReaderHighlightsViewController: UITableViewController {
|
||||
|
||||
private let filterControl = UISegmentedControl(items: ["全部", "注释", "划线"])
|
||||
|
||||
/// 空状态提示。放在 tableView 的子视图而不是 `backgroundView`:
|
||||
/// backgroundView 不参与无障碍层级遍历,VoiceOver 与 UI 测试都读不到它。
|
||||
private let emptyStateLabel = UILabel()
|
||||
|
||||
private var filteredHighlights: [RDEPUBHighlight] {
|
||||
switch filterControl.selectedSegmentIndex {
|
||||
case 1:
|
||||
@@ -120,19 +124,28 @@ final class RDEPUBReaderHighlightsViewController: UITableViewController {
|
||||
navigationController?.navigationBar.accessibilityIdentifier = "epub.reader.highlights.navbar"
|
||||
}
|
||||
|
||||
private func updateEmptyState() {
|
||||
guard filteredHighlights.isEmpty else {
|
||||
tableView.backgroundView = nil
|
||||
return
|
||||
}
|
||||
private func installEmptyStateLabelIfNeeded() {
|
||||
guard emptyStateLabel.superview == nil else { return }
|
||||
emptyStateLabel.textAlignment = .center
|
||||
emptyStateLabel.numberOfLines = 0
|
||||
emptyStateLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
emptyStateLabel.accessibilityIdentifier = "epub.reader.highlights.empty"
|
||||
emptyStateLabel.isAccessibilityElement = true
|
||||
tableView.addSubview(emptyStateLabel)
|
||||
// 固定在可视区域居中,不随内容滚动
|
||||
NSLayoutConstraint.activate([
|
||||
emptyStateLabel.centerXAnchor.constraint(equalTo: tableView.frameLayoutGuide.centerXAnchor),
|
||||
emptyStateLabel.centerYAnchor.constraint(equalTo: tableView.frameLayoutGuide.centerYAnchor),
|
||||
emptyStateLabel.leadingAnchor.constraint(greaterThanOrEqualTo: tableView.frameLayoutGuide.leadingAnchor, constant: 24),
|
||||
emptyStateLabel.trailingAnchor.constraint(lessThanOrEqualTo: tableView.frameLayoutGuide.trailingAnchor, constant: -24)
|
||||
])
|
||||
}
|
||||
|
||||
let label = UILabel()
|
||||
label.text = emptyStateText()
|
||||
label.textAlignment = .center
|
||||
label.textColor = theme.contentTextColor.withAlphaComponent(0.7)
|
||||
label.numberOfLines = 0
|
||||
label.accessibilityIdentifier = "epub.reader.highlights.empty"
|
||||
tableView.backgroundView = label
|
||||
private func updateEmptyState() {
|
||||
installEmptyStateLabelIfNeeded()
|
||||
emptyStateLabel.textColor = theme.contentTextColor.withAlphaComponent(0.7)
|
||||
emptyStateLabel.text = emptyStateText()
|
||||
emptyStateLabel.isHidden = !filteredHighlights.isEmpty
|
||||
}
|
||||
|
||||
@objc private func filterChangedAction() {
|
||||
@@ -209,6 +222,9 @@ final class RDEPUBReaderBookmarksViewController: UITableViewController {
|
||||
return formatter
|
||||
}()
|
||||
|
||||
/// 同 highlights 面板:不能用 `tableView.backgroundView`,那样无障碍读不到。
|
||||
private let emptyStateLabel = UILabel()
|
||||
|
||||
init(bookmarks: [RDEPUBBookmark], theme: RDEPUBReaderTheme) {
|
||||
self.bookmarks = bookmarks.sorted { $0.createdAt > $1.createdAt }
|
||||
self.theme = theme
|
||||
@@ -279,18 +295,27 @@ final class RDEPUBReaderBookmarksViewController: UITableViewController {
|
||||
navigationController?.navigationBar.accessibilityIdentifier = "epub.reader.bookmarks.navbar"
|
||||
}
|
||||
|
||||
private func installEmptyStateLabelIfNeeded() {
|
||||
guard emptyStateLabel.superview == nil else { return }
|
||||
emptyStateLabel.text = "暂无书签"
|
||||
emptyStateLabel.textAlignment = .center
|
||||
emptyStateLabel.numberOfLines = 0
|
||||
emptyStateLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
emptyStateLabel.accessibilityIdentifier = "epub.reader.bookmarks.empty"
|
||||
emptyStateLabel.isAccessibilityElement = true
|
||||
tableView.addSubview(emptyStateLabel)
|
||||
NSLayoutConstraint.activate([
|
||||
emptyStateLabel.centerXAnchor.constraint(equalTo: tableView.frameLayoutGuide.centerXAnchor),
|
||||
emptyStateLabel.centerYAnchor.constraint(equalTo: tableView.frameLayoutGuide.centerYAnchor),
|
||||
emptyStateLabel.leadingAnchor.constraint(greaterThanOrEqualTo: tableView.frameLayoutGuide.leadingAnchor, constant: 24),
|
||||
emptyStateLabel.trailingAnchor.constraint(lessThanOrEqualTo: tableView.frameLayoutGuide.trailingAnchor, constant: -24)
|
||||
])
|
||||
}
|
||||
|
||||
private func updateEmptyState() {
|
||||
guard bookmarks.isEmpty == false else {
|
||||
let label = UILabel()
|
||||
label.text = "暂无书签"
|
||||
label.textAlignment = .center
|
||||
label.textColor = theme.contentTextColor.withAlphaComponent(0.7)
|
||||
label.numberOfLines = 0
|
||||
label.accessibilityIdentifier = "epub.reader.bookmarks.empty"
|
||||
tableView.backgroundView = label
|
||||
return
|
||||
}
|
||||
tableView.backgroundView = nil
|
||||
installEmptyStateLabelIfNeeded()
|
||||
emptyStateLabel.textColor = theme.contentTextColor.withAlphaComponent(0.7)
|
||||
emptyStateLabel.isHidden = !bookmarks.isEmpty
|
||||
}
|
||||
|
||||
private func titleText(for bookmark: RDEPUBBookmark) -> String {
|
||||
|
||||
Reference in New Issue
Block a user