- PaginationCoordinator: 新增 textReflowable 快速进入路径,支持增量章节构建 与 staged 合并策略,避免整书一次性构建的主线程阻塞 - TextBookBuilder: 重构为支持单章构建与增量合并模式 - ChapterPageCounter: 优化分页计数逻辑,支持章节级分页缓存 - PageFrameFactory: 新增 CoreText 页面帧工厂,提升排版性能 - ReaderContext/ReaderRuntime: 适配增量分页状态管理 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
89 lines
4.6 KiB
Swift
89 lines
4.6 KiB
Swift
import UIKit
|
||
|
||
/// EPUB 阅读器界面组装协调器:负责阅读器初次启动时的 UI 搭建。
|
||
///
|
||
/// 职责:
|
||
/// - 组装阅读器视图层次结构(readerView、loadingIndicator、errorLabel)
|
||
/// - 注册内容视图类型
|
||
/// - 处理外部纯文本图书的启动收尾逻辑
|
||
final class RDEPUBReaderAssemblyCoordinator {
|
||
private unowned let context: RDEPUBReaderContext
|
||
|
||
init(context: RDEPUBReaderContext) {
|
||
self.context = context
|
||
}
|
||
|
||
/// 组装阅读器界面:添加 readerView、loadingIndicator、errorLabel 到控制器视图,并配置顶部工具栏。
|
||
func assembleInterface() {
|
||
guard let controller = context.controller,
|
||
let readerView = context.readerView else { return }
|
||
|
||
controller.view.backgroundColor = context.configuration.theme.contentBackgroundColor
|
||
setupReaderView(readerView, in: controller.view)
|
||
setupLoadingIndicator(controller.loadingIndicator, in: controller.view)
|
||
setupErrorLabel(controller.errorLabel, in: controller.view)
|
||
controller.delegate?.epubReader(controller, configureTopToolView: controller.topToolView)
|
||
print("[ReadViewDemo] assembleInterface: dataSource=\(readerView.dataSource != nil ? "set" : "nil"), numberOfPages=\(readerView.numberOfPages())")
|
||
}
|
||
|
||
/// 外部纯文本图书启动时,加载已保存的书签、高亮和阅读位置,完成分页收尾。
|
||
func finishExternalTextBookLaunchIfNeeded() {
|
||
guard let runtime = context.runtime,
|
||
let controller = context.controller,
|
||
context.isExternalTextBook else {
|
||
return
|
||
}
|
||
|
||
let restoreLocation = context.currentBookIdentifier.flatMap { context.persistence?.loadLocation(for: $0) }
|
||
if let id = context.currentBookIdentifier {
|
||
context.activeBookmarks = context.persistence?.loadBookmarks(for: id) ?? []
|
||
context.activeHighlights = context.persistence?.loadHighlights(for: id) ?? []
|
||
}
|
||
|
||
if let textBook = controller.textBook {
|
||
print("[ReadViewDemo] finishExternalTextBook: applying textBook with \(textBook.pages.count) pages")
|
||
runtime.applyTextBook(textBook, restoreLocation: restoreLocation)
|
||
print("[ReadViewDemo] finishExternalTextBook: after applyTextBook, numberOfPages=\(context.readerView?.numberOfPages() ?? -1)")
|
||
} else {
|
||
runtime.finishPagination(restoreLocation: restoreLocation)
|
||
}
|
||
}
|
||
|
||
private func setupReaderView(_ readerView: RDReaderView, in containerView: UIView) {
|
||
readerView.dataSource = context.controller as? RDReaderDataSource
|
||
readerView.delegate = context.controller as? RDReaderDelegate
|
||
readerView.translatesAutoresizingMaskIntoConstraints = false
|
||
containerView.addSubview(readerView)
|
||
NSLayoutConstraint.activate([
|
||
readerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||
readerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||
readerView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||
readerView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||
])
|
||
|
||
readerView.register(contentView: RDEPUBTextContentView.self, contentViewWithReuseIdentifier: NSStringFromClass(RDEPUBTextContentView.self))
|
||
readerView.register(contentView: RDEPUBWebContentView.self, contentViewWithReuseIdentifier: NSStringFromClass(RDEPUBWebContentView.self))
|
||
context.controller?.applyReaderViewConfiguration()
|
||
}
|
||
|
||
private func setupLoadingIndicator(_ loadingIndicator: UIActivityIndicatorView, in containerView: UIView) {
|
||
loadingIndicator.hidesWhenStopped = true
|
||
loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
|
||
containerView.addSubview(loadingIndicator)
|
||
NSLayoutConstraint.activate([
|
||
loadingIndicator.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
|
||
loadingIndicator.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
|
||
])
|
||
}
|
||
|
||
private func setupErrorLabel(_ errorLabel: UILabel, in containerView: UIView) {
|
||
errorLabel.translatesAutoresizingMaskIntoConstraints = false
|
||
containerView.addSubview(errorLabel)
|
||
NSLayoutConstraint.activate([
|
||
errorLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 24),
|
||
errorLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -24),
|
||
errorLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
|
||
])
|
||
}
|
||
}
|