Files
ReadViewSDK/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderContext.swift
T
shenandshen 948004eed1 docs: 补充注释、修正过时文档、清理重复内容
源码注释:
- 为 ~60 个 Swift 文件补充缺失的 doc comment(file header、类型、属性、方法)
- 修正 4 处错误注释:翻页模式数量、搜索行为描述、手势识别器描述、悬空文档块

文档维护:
- 删除重复文档:WXRead/读书EPUB阅读器实现架构.md(与微信读书版完全一致)
- 合并重叠文档:阅读器规划.md → 阅读器功能开发计划.md(单一真值)
- 修正过时内容:所有文档中"四种翻页模式"→"三种",移除 horizontalCoverScroll
- 更新架构图:补齐 EPUBUI/ReaderController、Paging/、Typesetter/ 等子目录
- 更新 index.md 索引:新增开发计划和架构对比文档引用
2026-06-01 09:33:23 +08:00

262 lines
9.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import UIKit
/// 阅读器共享状态中心:所有 coordinator 通过 context 访问业务状态和便捷方法。
///
/// context 持有:
/// - 业务状态(parser、publication、textBook、pages 等)
/// - UI 配置(configuration、brightness
/// - 持久化策略(persistence
/// - 便捷方法(renderStyle、layoutConfig 等)
/// - 弱引用 controller(仅用于 UIKit 呈现操作)
final class RDEPUBReaderContext {
// MARK: - 引用
/// 弱引用阅读器控制器,用于 UIKit 呈现操作。
weak var controller: RDEPUBReaderController?
/// 弱引用阅读器视图,用于布局和页面状态查询。
weak var readerView: RDReaderView?
/// 依赖注入容器,提供解析器、分页器等工厂方法。
var dependencies: RDEPUBReaderDependencies = .live
/// 便捷访问当前控制器的运行时协调器集合。
var runtime: RDEPUBReaderRuntime? {
controller?.runtime
}
// MARK: - 业务状态
/// EPUB 解析器实例。
var parser: RDEPUBParser?
/// 解析后的出版物模型。
var publication: RDEPUBPublication?
/// 当前阅读会话,管理页面和章节状态。
var readingSession: RDEPUBReadingSession?
/// 原生文本排版生成的图书模型(仅文本重排模式)。
var textBook: RDEPUBTextBook?
/// 当前书籍的所有书签。
var activeBookmarks: [RDEPUBBookmark] = []
/// 当前书籍的所有高亮标注。
var activeHighlights: [RDEPUBHighlight] = []
/// 当前打开书籍的唯一标识。
var currentBookIdentifier: String?
/// 分页操作令牌,用于取消过期的异步分页任务。
var paginationToken = UUID()
/// Web 内容分页计算器。
var paginator: RDEPUBPaginator?
/// 全文搜索状态。
var searchState: RDEPUBSearchState?
/// 上次文本分页时的页面尺寸,用于检测是否需要重新分页。
var lastTextPaginationPageSize: CGSize?
/// 当前用户文本选区。
var currentSelection: RDEPUBSelection?
// MARK: - 控制器状态(从 controller 下沉)
/// 阅读器配置(字号、字体、主题等)。
var configuration: RDEPUBReaderConfiguration = .default
/// 持久化策略,负责书签、高亮、阅读位置的存取。
var persistence: RDEPUBReaderPersistence?
/// 当前打开的 EPUB 文件 URL。
var epubURL: URL = URL(string: "about:blank")!
/// 是否正在重新分页。
var isRepaginating: Bool = false
/// 是否已完成首次加载。
var didStartInitialLoad: Bool = false
/// 是否为外部传入的纯文本图书。
var isExternalTextBook: Bool = false
/// 外部纯文本文件的 URL。
var textFileURL: URL?
/// 文本图书缓存,避免重复排版。
var textBookCache = RDEPUBTextBookCache()
// MARK: - 初始化
init(controller: RDEPUBReaderController) {
self.controller = controller
self.readerView = controller.readerView
}
// MARK: - 便捷方法
/// 根据当前 readerView 和控制器尺寸构建布局上下文。
func currentLayoutContext() -> RDEPUBNavigatorLayoutContext {
let containerSize = readerView?.bounds.size ?? .zero
let viewSize = controller?.view.bounds.size ?? containerSize
let resolvedSize = containerSize == .zero ? viewSize : containerSize
return RDEPUBNavigatorLayoutContext(
containerSize: resolvedSize,
pagesPerScreen: readerView?.pagesPerScreen ?? 1,
safeAreaInsets: controller?.view.safeAreaInsets ?? .zero,
userInterfaceIdiom: controller?.traitCollection.userInterfaceIdiom ?? .phone,
reflowableContentInsets: configuration.reflowableContentInsets
)
}
/// 根据当前配置生成阅读偏好设置。
func currentPreferences() -> RDEPUBPreferences {
configuration.makePreferences()
}
/// 获取当前文本排版的单页尺寸,优先从 readerView 解析,兜底用布局上下文。
func currentTextPageSize() -> CGSize {
let pageNum = (readerView?.currentPage ?? -1) >= 0 ? readerView?.currentPage : nil
if let readerView, let pageNum {
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
if resolvedSize.width > 0, resolvedSize.height > 0 {
return resolvedSize
}
}
return currentLayoutContext().viewportSize
}
/// 根据当前配置生成文本渲染样式(字体、行距、颜色)。
func currentTextRenderStyle() -> RDEPUBTextRenderStyle {
let font = configuration.fontChoice.font(ofSize: configuration.fontSize)
let lineSpacing = max(font.lineHeight * (configuration.lineHeightMultiple - 1), 4)
return RDEPUBTextRenderStyle(
font: font,
lineSpacing: lineSpacing,
textColor: configuration.theme.contentTextColor,
backgroundColor: configuration.theme.contentBackgroundColor
)
}
/// 根据页面尺寸和配置生成文本排版参数。
func currentTextLayoutConfig(pageSize: CGSize) -> RDEPUBTextLayoutConfig {
return RDEPUBTextLayoutConfig(
frameWidth: max(pageSize.width, 1),
frameHeight: max(pageSize.height, 1),
edgeInsets: configuration.reflowableContentInsets,
numberOfColumns: configuration.numberOfColumns,
columnGap: configuration.columnGap,
avoidOrphans: true,
avoidWidows: true,
avoidPageBreakInsideEnabled: true,
hyphenation: true,
imageMaxHeightRatio: 0.85,
fallbackViewportSize: dependencies.environment.fallbackViewportSize
)
}
/// 获取当前配置对应的文本渲染器实例。
func resolvedTextRenderer() -> RDEPUBTextRenderer {
dependencies.makeTextRenderer(configuration.textRenderingEngine)
}
/// 当前活跃的页面列表。
var activePages: [EPUBPage] {
readingSession?.activePages ?? []
}
/// 当前活跃的章节信息列表。
var activeChapters: [EPUBChapterInfo] {
readingSession?.activeChapters ?? []
}
/// 屏幕亮度代理属性,读写均转发给系统环境。
var currentBrightness: CGFloat {
get { dependencies.environment.currentBrightness }
set { dependencies.environment.currentBrightness = newValue }
}
/// 用新快照替换当前活跃的分页快照。
func replaceActiveSnapshot(_ snapshot: RDEPUBReadingSession.PaginationSnapshot) {
readingSession?.setActiveSnapshot(snapshot)
}
/// 清除当前活跃快照,重置运行时状态。
func clearActiveSnapshot() {
readingSession?.resetRuntimeState()
}
/// 工厂方法:创建 EPUB 解析器。
func makeParser() -> RDEPUBParser {
dependencies.makeParser()
}
/// 工厂方法:创建 Web 内容分页计算器。
func makePaginator() -> RDEPUBPaginator {
dependencies.makePaginator()
}
/// 工厂方法:创建 EPUB 文本图书构建器。
func makeTextBookBuilder(layoutConfig: RDEPUBTextLayoutConfig) -> RDEPUBTextBookBuilder {
dependencies.makeTextBookBuilder(resolvedTextRenderer(), textBookCache, layoutConfig)
}
/// 工厂方法:创建纯文本图书构建器。
func makePlainTextBookBuilder(layoutConfig: RDEPUBTextLayoutConfig) -> RDPlainTextBookBuilder {
dependencies.makePlainTextBookBuilder(resolvedTextRenderer(), layoutConfig)
}
/// 获取当前可见页面的阅读位置。
func currentVisibleLocation() -> RDEPUBLocation? {
controller?.currentVisibleLocation()
}
/// 从持久化存储加载上次保存的阅读位置。
func persistenceLocation() -> RDEPUBLocation? {
guard let currentBookIdentifier else { return nil }
return persistence?.loadLocation(for: currentBookIdentifier)
}
/// 将阅读位置持久化到存储。
func persist(location: RDEPUBLocation) {
guard let currentBookIdentifier else { return }
persistence?.saveLocation(location, for: currentBookIdentifier)
}
/// 根据规范化 href 获取文本章节数据。
func textChapterData(forNormalizedHref href: String) -> RDEPUBChapterData? {
guard let textBook, let publication else { return nil }
let normalizedHref = publication.resourceResolver.normalizedHref(href) ?? href
return textBook.chapters.lazy
.first(where: { (publication.resourceResolver.normalizedHref($0.href) ?? $0.href) == normalizedHref })
.flatMap { textBook.chapterData(for: $0.href) }
}
/// 显示加载指示器。
func showLoading() {
controller?.showLoading()
}
/// 隐藏加载指示器。
func hideLoading() {
controller?.hideLoading()
}
/// 将错误转发给控制器处理。
func handle(error: Error) {
controller?.handle(error: error)
}
/// 触发工具栏状态更新。
func updateReaderChrome() {
controller?.updateReaderChrome()
}
/// 刷新可见内容并保持当前阅读位置不变。
func refreshVisibleContentPreservingLocation() {
controller?.refreshVisibleContentPreservingLocation()
}
/// 恢复到指定阅读位置。
func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool = false) -> Bool {
controller?.restoreReadingLocation(location, animated: animated) ?? false
}
/// 重新分页并保持当前阅读位置。
func repaginatePreservingCurrentLocation() {
controller?.repaginatePreservingCurrentLocation()
}
/// 将当前配置应用到阅读器视图。
func applyReaderViewConfiguration() {
controller?.applyReaderViewConfiguration()
}
/// 同步书签按钮的 UI 状态。
func updateBookmarkChrome() {
controller?.updateBookmarkChrome()
}
}