feat(reader): 增强阅读器功能与 UI 测试支持

- 新增字体选择(系统/宋体/圆体/等宽)与暗色图片柔化配置
- 文本选择改为自定义手势+操作栏(拷贝/高亮/批注)
- 添加 accessibilityIdentifier 支持自动化 UI 测试
- 新增 UITests 覆盖阅读器打开/关闭、工具栏、设置面板、批注等
- 添加 Demo 测试用 EPUB 书源(宝山辽墓材料与释读)
- 新增文档:UI 自动化测试、功能开发计划、阅读器规划
This commit is contained in:
shen
2026-05-31 23:56:54 +08:00
parent 44202357c0
commit 1efb9d172f
268 changed files with 4991 additions and 44 deletions
@@ -31,6 +31,16 @@ public final class RDURLReaderController: UIViewController {
private let epubConfiguration: RDEPUBReaderConfiguration
///
private var embeddedController: UIViewController?
private let demoStateLabel: UILabel = {
let label = UILabel()
label.accessibilityIdentifier = "demo.reader.state"
label.font = .systemFont(ofSize: 1)
label.textColor = .clear
label.alpha = 0.01
label.isAccessibilityElement = true
label.text = "reader=opening"
return label
}()
///
/// - Parameters:
@@ -56,13 +66,19 @@ public final class RDURLReaderController: UIViewController {
view.backgroundColor = .systemBackground
title = bookURL.deletingPathExtension().lastPathComponent
embedReaderController()
installDemoStateLabel()
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
emitDemoState()
}
/// Demo
/// - Parameter displayType:
public func applyDemoDisplayType(_ displayType: RDReaderView.DisplayType) {
readerController?.configuration.displayType = displayType
logDemoState(prefix: "display=\(displayType.demoArgumentValue)")
emitDemoState(prefix: "display=\(displayType.demoArgumentValue)")
}
/// Demo
@@ -74,7 +90,7 @@ public final class RDURLReaderController: UIViewController {
public func goToDemoPage(_ pageNumber: Int, animated: Bool = false) -> Bool {
let moved = readerController?.go(toPageNumber: pageNumber, animated: animated) ?? false
if moved {
logDemoState(prefix: "page=\(pageNumber)")
emitDemoState(prefix: "page=\(pageNumber)")
}
return moved
}
@@ -162,6 +178,8 @@ public final class RDURLReaderController: UIViewController {
controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
controller.didMove(toParent: self)
readerController?.delegate = self
emitDemoState()
}
/// EPUB
@@ -179,6 +197,32 @@ public final class RDURLReaderController: UIViewController {
print("[ReadViewDemo] automation \(prefix) -> page \(page) href \(href) progression \(progression)")
}
private func installDemoStateLabel() {
view.addSubview(demoStateLabel)
demoStateLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
demoStateLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
demoStateLabel.topAnchor.constraint(equalTo: view.topAnchor),
demoStateLabel.widthAnchor.constraint(equalToConstant: 1),
demoStateLabel.heightAnchor.constraint(equalToConstant: 1)
])
}
private func emitDemoState(prefix: String? = nil) {
let page = readerController?.currentPageNumber.map(String.init) ?? "nil"
let display = readerController?.configuration.displayType.demoArgumentValue ?? epubConfiguration.displayType.demoArgumentValue
let toolbar = readerController?.readerView.isShowToolView == true ? "visible" : "hidden"
let highlights = readerController?.highlights.count ?? 0
let selection = readerController?.currentSelection == nil ? 0 : 1
let state = "reader=opened page=\(page) display=\(display) toolbar=\(toolbar) highlights=\(highlights) selection=\(selection)"
demoStateLabel.text = state
if let prefix {
logDemoState(prefix: prefix)
} else {
print("[ReadViewDemo] automation \(state)")
}
}
///
/// 使 viewport layoutConfig.edgeInsets
///
@@ -188,7 +232,7 @@ public final class RDURLReaderController: UIViewController {
///
private func currentTextRenderStyle() -> RDEPUBTextRenderStyle {
let font = UIFont.systemFont(ofSize: epubConfiguration.fontSize)
let font = epubConfiguration.fontChoice.font(ofSize: epubConfiguration.fontSize)
let lineSpacing = max(font.lineHeight * (epubConfiguration.lineHeightMultiple - 1), 4)
return RDEPUBTextRenderStyle(
font: font,
@@ -214,6 +258,20 @@ public final class RDURLReaderController: UIViewController {
}
}
extension RDURLReaderController: RDEPUBReaderDelegate {
public func epubReader(_ reader: UIViewController, didUpdateLocation location: RDEPUBLocation) {
emitDemoState()
}
public func epubReader(_ reader: UIViewController, didChangeSelection selection: RDEPUBSelection?) {
emitDemoState(prefix: "selection=\(selection == nil ? 0 : 1)")
}
public func epubReader(_ reader: UIViewController, didUpdateHighlights highlights: [RDEPUBHighlight]) {
emitDemoState(prefix: "highlights=\(highlights.count)")
}
}
/// Demo
private extension RDReaderView.DisplayType {
/// Demo