# EPUBUI 模块代码级参考文档 > 最后更新:2026-06-18 --- ## 1. 模块概述 `EPUBUI` 是 ReadViewSDK 的**用户界面层**,位于架构最顶层。它包含阅读器控制器、协调器模式、章节运行时系统、设置面板、文本页面渲染和标注管理等子系统。 **文件清单(~60 个 Swift 文件):** | 子系统 | 目录 | 核心文件 | 职责 | |--------|------|----------|------| | **阅读器控制器** | `EPUBUI/` | `RDEPUBReaderController*.swift` | 主控制器及其扩展 | | **协调器** | `ReaderController/` | `RDEPUBReader*Coordinator.swift` | 各功能域协调器 | | **运行时** | `ReaderController/` | `RDEPUBReaderRuntime.swift`, `RDEPUBReaderContext.swift` | 运行时状态与依赖 | | **章节运行时** | `ReaderController/ChapterRuntime/` | `RDEPUBChapter*.swift` | 按需加载、缓存、页图 | | **设置** | `Settings/` | `RDEPUBReaderSettings*.swift` | 配置、主题、设置面板 | | **文本页面** | `TextPage/` | `RDEPUBTextContentView*.swift` | 原生文本渲染页面 | | **UI 组件** | `EPUBUI/` | `RDEPUBReader*ToolView.swift` | 工具栏、搜索栏、目录 | | **笔记弹层** | `Notes/` | `RDEPUBNotePopup*.swift` | 脚注弹层 | | **WebView** | `EPUBUI/` | `RDEPUBWebContentView.swift` | WebView 内容页面 | --- ## 2. 阅读器控制器 ### 2.1 RDEPUBReaderController **文件:** `RDEPUBReaderController.swift` + 扩展文件 主控制器,`UIViewController` 子类,是整个阅读器的入口。 ```swift public final class RDEPUBReaderController: UIViewController { public weak var delegate: RDEPUBReaderDelegate? public var configuration: RDEPUBReaderConfiguration // 配置(变化时自动重新分页/刷新) public var currentLocation: RDEPUBLocation? // 当前阅读位置 public var currentPageNumber: Int? // 当前页码(1-based) public var currentSelection: RDEPUBSelection? // 当前选中文本 public var highlights: [RDEPUBHighlight] // 高亮列表 public var bookmarks: [RDEPUBBookmark] // 书签列表 public var annotations: [RDEPUBAnnotation] // 标注列表(高亮+书签,按时间排序) public var tableOfContents: [EPUBTableOfContentsItem] // 目录树 public var flattenedTableOfContents: [RDEPUBReaderTableOfContentsItem] // 扁平化目录 let epubURL: URL // EPUB 文件路径 let persistence: RDEPUBReaderPersistence? // 持久化代理 let dependencies: RDEPUBReaderDependencies // 依赖注入 let readerView = RDEpubReaderView() // 翻页容器 } ``` **扩展文件职责:** | 文件 | 职责 | |------|------| | `RDEPUBReaderController+PublicAPI.swift` | 公开 API(跳转、搜索、标注、书签) | | `RDEPUBReaderController+DataSource.swift` | `RDEpubReaderPageProvider` 实现 | | `RDEPUBReaderController+ContentDelegates.swift` | WebView/TextContentView 代理路由 | | `RDEPUBReaderController+LocationResolution.swift` | 页码/位置解析、阅读状态同步 | | `RDEPUBReaderController+ExternalLinks.swift` | 外部链接处理(白名单、确认弹窗) | | `RDEPUBReaderController+AttachmentTooltip.swift` | 附件 alt 文本 tooltip 展示 | | `RDEPUBReaderController+RenderSupport.swift` | 渲染辅助(WebView/TextContent 创建) | | `RDEPUBReaderController+RuntimeBridge.swift` | Runtime 桥接 | | `RDEPUBReaderController+TableOfContents.swift` | 目录处理 | ### 2.2 RDEPUBReaderDelegate **文件:** `RDEPUBReaderDelegate.swift` ```swift public protocol RDEPUBReaderDelegate: AnyObject { func epubReader(_ reader: UIViewController, didOpen publication: RDEPUBPublication) func epubReader(_ reader: UIViewController, didUpdateLocation location: RDEPUBLocation) func epubReaderDidReachEnd(_ reader: UIViewController) func epubReader(_ reader: UIViewController, didChangeSelection selection: RDEPUBSelection?) func epubReader(_ reader: UIViewController, didUpdateHighlights highlights: [RDEPUBHighlight]) func epubReader(_ reader: UIViewController, didUpdateBookmarks bookmarks: [RDEPUBBookmark]) func epubReader(_ reader: UIViewController, didUpdateSearchResult result: RDEPUBSearchResult?) func epubReader(_ reader: UIViewController, didChangeCurrentSearchMatch match: RDEPUBSearchMatch?) func epubReader(_ reader: UIViewController, didUpdateCurrentTableOfContentsItem item: RDEPUBReaderTableOfContentsItem?) func epubReader(_ reader: UIViewController, didActivateExternalLink url: URL) func epubReader(_ reader: UIViewController, shouldOpenExternalURL url: URL) -> Bool func epubReader(_ reader: UIViewController, didFailWithError error: Error) func epubReader(_ reader: UIViewController, configureTopToolView topToolView: RDEPUBReaderTopToolView) } ``` 所有方法均有默认空实现。 ### 2.3 RDEPUBReaderPersistence **文件:** `RDEPUBReaderPersistence.swift` 持久化协议,由宿主 App 实现。 ```swift public protocol RDEPUBReaderPersistence: AnyObject { func loadLocation(for bookIdentifier: String) -> RDEPUBLocation? func saveLocation(_ location: RDEPUBLocation, for bookIdentifier: String) func loadBookmarks(for bookIdentifier: String) -> [RDEPUBBookmark] func saveBookmarks(_ bookmarks: [RDEPUBBookmark], for bookIdentifier: String) func loadHighlights(for bookIdentifier: String) -> [RDEPUBHighlight] func saveHighlights(_ highlights: [RDEPUBHighlight], for bookIdentifier: String) } ``` --- ## 3. 运行时与上下文 ### 3.1 RDEPUBReaderContext **文件:** `ReaderController/RDEPUBReaderContext.swift` 阅读器的共享上下文,持有所有运行时状态。协调器通过 `unowned` 引用访问。 ```swift final class RDEPUBReaderContext { weak var controller: RDEPUBReaderController? weak var readerView: RDEpubReaderView? var dependencies: RDEPUBReaderDependencies var runtime: RDEPUBReaderRuntime? // 解析状态 var parser: RDEPUBParser? var publication: RDEPUBPublication? var readingSession: RDEPUBReadingSession? var textBook: RDEPUBTextBook? var bookPageMap: RDEPUBBookPageMap? // 标注状态 var activeBookmarks: [RDEPUBBookmark] var activeHighlights: [RDEPUBHighlight] var currentBookIdentifier: String? var selectionState: RDEPUBSelectionState var currentSelection: RDEPUBSelection? // 搜索状态 var searchState: RDEPUBSearchState? } ``` ### 3.2 RDEPUBReaderDependencies **文件:** `ReaderController/RDEPUBReaderDependencies.swift` 依赖注入容器,支持测试替身。 ```swift public struct RDEPUBReaderDependencies { public var environment: any RDEPUBReaderDisplayEnvironment public var makeParser: () -> RDEPUBParser public var makePaginator: () -> RDEPUBPaginator public var makeTextBookBuilder: (RDEPUBTextRenderer, RDEPUBTextBookCache?, RDEPUBTextLayoutConfig) -> RDEPUBTextBookBuilder public var makePlainTextBookBuilder: (RDEPUBTextRenderer, RDEPUBTextLayoutConfig) -> RDEpubPlainTextBookBuilder public var makeTextRenderer: (RDEPUBTextRenderingEngine) -> RDEPUBTextRenderer public static var live: RDEPUBReaderDependencies // 默认实现 } ``` ### 3.3 RDEPUBReaderRuntime **文件:** `ReaderController/RDEPUBReaderRuntime.swift` 运行时管理器,持有所有协调器和子系统。 ```swift final class RDEPUBReaderRuntime { lazy var chapterRuntimeStore = RDEPUBChapterRuntimeStore() lazy var summaryDiskCache: RDEPUBChapterSummaryDiskCache lazy var chapterLoader: RDEPUBChapterLoader lazy var pageResolver: RDEPUBPageResolver lazy var loadCoordinator: RDEPUBReaderLoadCoordinator lazy var paginationCoordinator: RDEPUBReaderPaginationCoordinator lazy var locationCoordinator: RDEPUBReaderLocationCoordinator lazy var searchCoordinator: RDEPUBReaderSearchCoordinator lazy var chromeCoordinator: RDEPUBReaderChromeCoordinator lazy var annotationCoordinator: RDEPUBReaderAnnotationCoordinator lazy var viewportMonitor: RDEPUBReaderViewportMonitor lazy var jumpSessionManager: RDEPUBJumpSessionManager lazy var backgroundPriorityManager: RDEPUBBackgroundPriorityManager lazy var backgroundCoverageStore: RDEPUBBackgroundCoverageStore lazy var reconciliationCoordinator: RDEPUBPageMapReconciliationCoordinator var isSettingsPanelOpen: Bool var needsFullRepaginationAfterSettingsClose: Bool } ``` --- ## 4. 协调器子系统 采用 **Coordinator 模式**,每个功能域由独立的协调器管理。 ### 4.1 RDEPUBReaderLoadCoordinator **文件:** `ReaderController/RDEPUBReaderLoadCoordinator.swift` 负责初始加载流程:解析 EPUB → 创建 Publication → 恢复阅读位置。 ```swift final class RDEPUBReaderLoadCoordinator { func startInitialLoadIfNeeded() // 开始初始加载 func loadPublication() // 解析 EPUB 文件 func applyParsedPublication(...) // 应用解析结果 } ``` ### 4.2 RDEPUBReaderPaginationCoordinator **文件:** `ReaderController/RDEPUBReaderPaginationCoordinator.swift` 负责分页调度:WebView 分页 → 文本构建 → 页图生成。 ```swift final class RDEPUBReaderPaginationCoordinator { func startPagination(...) // 开始分页 func applyPageCounts(...) // 应用页数结果 func repaginatePreservingCurrentLocation() // 重新分页(保持位置) } ``` ### 4.3 RDEPUBReaderLocationCoordinator **文件:** `ReaderController/RDEPUBReaderLocationCoordinator.swift` 负责位置管理:恢复位置、记录位置变化、目录跳转。 ```swift final class RDEPUBReaderLocationCoordinator { func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool, ...) -> Bool func currentVisibleLocation() -> RDEPUBLocation? func recordPageChangeIfNeeded() } ``` ### 4.4 RDEPUBReaderSearchCoordinator **文件:** `ReaderController/RDEPUBReaderSearchCoordinator.swift` 负责搜索管理:执行搜索、导航到匹配项、清除搜索。 ```swift final class RDEPUBReaderSearchCoordinator { func search(keyword: String) func searchNext() -> Bool func searchPrevious() -> Bool func selectSearchMatch(at index: Int) -> Bool func clearSearch() } ``` ### 4.5 RDEPUBReaderChromeCoordinator **文件:** `ReaderController/RDEPUBReaderChromeCoordinator.swift` 负责 UI Chrome(工具栏、搜索栏)的创建和状态更新。 ```swift final class RDEPUBReaderChromeCoordinator { func makeTopToolView() -> RDEPUBReaderTopToolView func makeBottomToolView() -> RDEPUBReaderBottomToolView func updateReaderChrome() func toggleSearchBar() func presentTableOfContents() func presentSettings() } ``` ### 4.6 RDEPUBReaderAnnotationCoordinator **文件:** `ReaderController/RDEPUBReaderAnnotationCoordinator.swift` 负责标注管理:高亮、书签的增删改查。 ```swift final class RDEPUBReaderAnnotationCoordinator { func addHighlight(from selection: RDEPUBSelection?, color: String, note: String?) -> RDEPUBHighlight? func removeHighlight(withID id: String) func addBookmark(for location: RDEPUBLocation, ...) -> RDEPUBBookmark? func removeBookmark(withID id: String) func toggleBookmark() -> Bool func updateCurrentSelection(_ selection: RDEPUBSelection?) } ``` --- ## 5. 章节运行时子系统 **目录:** `ReaderController/ChapterRuntime/` 按需加载章节、管理章节缓存、维护全书页图。 ### 5.1 RDEPUBChapterRuntimeStore **文件:** `ChapterRuntime/RDEPUBChapterRuntimeStore.swift` 章节数据的核心存储,管理内存缓存和窗口。 ```swift final class RDEPUBChapterRuntimeStore { let chapterLoadQueue: DispatchQueue // 后台加载队列 private(set) var currentSpineIndex: Int? private(set) var windowSpineIndices: [Int] // 当前窗口内的 spine 索引 func chapterData(for spineIndex: Int) -> RDEPUBRuntimeChapter? func insertChapter(_ chapter: RDEPUBRuntimeChapter) func setCurrentChapter(spineIndex: Int, totalSpineCount: Int, windowRadius: Int) func evictableSpineIndices() -> [Int] func evict(spineIndex: Int) } ``` ### 5.2 RDEPUBChapterLoader **文件:** `ChapterRuntime/RDEPUBChapterLoader.swift` 章节按需加载器,支持优先级和磁盘摘要缓存。 ```swift final class RDEPUBChapterLoader { enum LoadPriority { case navigation // 导航(最高优先级) case preview // 预览 case prefetch // 预取(最低优先级) } func loadChapter( spineIndex: Int, store: RDEPUBChapterRuntimeStore, priority: LoadPriority, completion: @escaping (Result) -> Void ) } ``` **加载流程:** 1. 检查内存缓存(`RDEPUBChapterDataCache`) 2. 检查页数缓存(`RDEPUBPageCountCache`) 3. 检查磁盘摘要缓存(`RDEPUBChapterSummaryDiskCache`) 4. 构建章节(排版 + 分页) 5. 写入缓存 ### 5.3 RDEPUBChapterWindowCoordinator **文件:** `ChapterRuntime/RDEPUBChapterWindowCoordinator.swift` 管理章节窗口(当前章 + 前后各 N 章),协调加载和驱逐。 ```swift final class RDEPUBChapterWindowCoordinator { private(set) var currentSnapshot: RDEPUBChapterWindowSnapshot? var onSnapshotChanged: ((RDEPUBChapterWindowSnapshot) -> Void)? func openBook(at targetSpineIndex: Int, restoreChapterOffset: Int?) func navigateToChapter(at spineIndex: Int) } ``` ### 5.4 RDEPUBBookPageMap **文件:** `ChapterRuntime/RDEPUBBookPageMap.swift` 全书页图,映射绝对页码 ↔ spine 索引 + 本地页码。 ```swift struct RDEPUBBookPageMap { let entries: [RDEPUBBookPageMapEntry] let totalPages: Int func absolutePageIndex(spineIndex: Int, localPageIndex: Int) -> Int? func spineIndex(forAbsolutePage absolutePage: Int) -> Int? func localPageIndex(forAbsolutePage absolutePage: Int) -> Int? func chapterIndex(forSpineIndex spineIndex: Int) -> Int? } ``` ### 5.5 RDEPUBPageResolver **文件:** `ChapterRuntime/RDEPUBPageResolver.swift` 将绝对页码解析为具体的页面数据。 ```swift final class RDEPUBPageResolver { func resolvePage(absolutePageIndex: Int) -> RDEPUBResolvedPage? } struct RDEPUBResolvedPage { let page: RDEPUBTextPage let chapter: RDEPUBRuntimeChapter let chapterIndex: Int } ``` ### 5.6 其他 ChapterRuntime 组件 | 文件 | 职责 | |------|------| | `RDEPUBRuntimeChapter.swift` | 运行时章节模型(包含分页后的页面列表) | | `RDEPUBRuntimePageCount.swift` | 页数缓存模型 | | `RDEPUBChapterDataCache.swift` | 章节数据内存缓存(NSCache) | | `RDEPUBPageCountCache.swift` | 页数缓存 | | `RDEPUBChapterCacheKey.swift` | 缓存键(pageSize + fontSize + lineHeight) | | `RDEPUBChapterSummaryDiskCache.swift` | 章节摘要磁盘缓存 | | `RDEPUBChapterWindowSnapshot.swift` | 窗口快照(当前可见章节集合) | | `RDEPUBChapterLocation.swift` | 章节位置模型 | | `RDEPUBChapterOffsetMap.swift` | 章节偏移映射 | | `RDEPUBBackgroundTrace.swift` | 后台任务追踪日志 | | `RDEPUBMetadataParseWorker.swift` | 后台元数据解析 worker | | `RDEPUBMetadataParseCancellationController.swift` | 解析取消控制器 | --- ## 6. 设置子系统 **目录:** `Settings/` ### 6.1 RDEPUBReaderConfiguration **文件:** `Settings/RDEPUBReaderConfiguration.swift` 阅读器配置模型。 ```swift public struct RDEPUBReaderConfiguration: Equatable { public var fontSize: CGFloat public var lineHeightMultiple: CGFloat public var fontChoice: RDEPUBReaderFontChoice public var numberOfColumns: Int public var columnGap: CGFloat // ... 更多配置项 } public enum RDEPUBReaderFontChoice: String, Codable, CaseIterable { case system // 系统字体 case serif // 宋体 case rounded // 圆体 case monospaced // 等宽 } public enum RDEPUBTextRenderingEngine: Equatable { case dtCoreText } ``` ### 6.2 RDEPUBReaderSettings **文件:** `Settings/RDEPUBReaderSettings.swift` 设置状态管理。 ### 6.3 RDEPUBReaderSettingsViewController **文件:** `Settings/RDEPUBReaderSettingsViewController.swift` 设置面板 VC(字体大小、行高、字体选择、主题等)。 ### 6.4 RDEPUBReaderTheme **文件:** `Settings/RDEPUBReaderTheme.swift` 主题定义(日间/夜间/护眼等)。 --- ## 7. 文本页面子系统 **目录:** `TextPage/` 原生文本渲染模式(`textReflowable`)的页面视图。 ### 7.1 RDEPUBTextContentView **文件:** `TextPage/RDEPUBTextContentView.swift` 文本内容视图,使用 CoreText 渲染 `NSAttributedString`。 ```swift final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { weak var delegate: RDEPUBTextContentViewDelegate? func configure(page: RDEPUBTextPage, ...) func applyHighlights(_ highlights: [RDEPUBHighlight]) func applySearchState(_ state: RDEPUBSearchState?) } ``` ### 7.2 RDEPUBTextPageRenderView **文件:** `TextPage/RDEPUBTextPageRenderView.swift` CoreText 渲染视图,绘制 `NSAttributedString` 到屏幕上。 ### 7.3 RDEPUBTextAnnotationOverlay **文件:** `TextPage/RDEPUBTextAnnotationOverlay.swift` 标注叠加层,绘制高亮和下划线。 ### 7.4 RDEPUBSelectionOverlayView **文件:** `TextPage/RDEPUBSelectionOverlayView.swift` 文本选择叠加层(选择手柄)。 ### 7.5 RDEPUBTextSelectionController **文件:** `TextPage/RDEPUBTextSelectionController.swift` 文本选择手势控制器。 ### 7.6 RDEPUBPageInteractionController **文件:** `TextPage/RDEPUBPageInteractionController.swift` 页面交互控制器(长按选择、点击标注等)。 ### 7.7 RDEPUBPageLayoutSnapshot **文件:** `TextPage/RDEPUBPageLayoutSnapshot.swift` 页面布局快照(用于选择定位)。 ### 7.8 RDEPUBTextPageDecorationView **文件:** `TextPage/RDEPUBTextPageDecorationView.swift` 页面装饰视图(页码、页眉等)。 --- ## 8. UI 组件 ### 8.1 工具栏 | 文件 | 职责 | |------|------| | `RDEPUBReaderTopToolView.swift` | 顶部工具栏(返回、搜索、书签) | | `RDEPUBReaderBottomToolView.swift` | 底部工具栏(目录、书签、高亮、设置) | | `RDEPUBReaderToolView.swift` | 工具栏基类 | ### 8.2 搜索栏 **文件:** `RDEPUBReaderSearchBarView.swift` 搜索输入栏(输入框 + 上一个/下一个 + 关闭)。 ### 8.3 目录 **文件:** `RDEPUBReaderChapterListController.swift` 目录列表 VC,支持多级目录展开。 **文件:** `RDEPUBReaderTableOfContentsItem.swift` 目录项模型。 ### 8.4 高亮管理 **文件:** `RDEPUBReaderHighlightsViewController.swift` 高亮列表 VC。 ### 8.5 笔记弹层 **目录:** `Notes/` | 文件 | 职责 | |------|------| | `RDEPUBNotePopupCoordinator.swift` | 脚注弹层协调器 | | `RDEPUBNotePopupViewController.swift` | 脚注弹层 VC | ### 8.6 WebView 内容视图 **文件:** `RDEPUBWebContentView.swift` WebView 模式的内容页面视图。 ### 8.7 装饰叠加层 **文件:** `RDEPUBWebDecorationOverlayView.swift` WebView 模式的装饰叠加层(高亮、搜索高亮)。 --- ## 9. 状态模型 ### 9.1 RDEPUBSelectionState ```swift enum RDEPUBSelectionState: Equatable { case idle // 无选择 case selecting(anchor: Int) // 选择中 case selected(RDEPUBSelection) // 已选择 case committingAction(RDEPUBSelection, action: RDEPUBAnnotationMenuAction) // 执行操作中 } ``` ### 9.2 RDEPUBReaderUIState ```swift struct RDEPUBReaderUIState { let canToggleBookmark: Bool let hasBookmarkAtCurrentLocation: Bool let canShowBookmarks: Bool let canAddHighlight: Bool let canShowHighlights: Bool let showsTableOfContents: Bool let allowsHighlights: Bool let showsSettingsPanel: Bool } ``` ### 9.3 RDEPUBViewportTypes **文件:** `RDEPUBViewportTypes.swift` 视口相关模型。 --- ## 10. 其他组件 ### 10.1 RDEPUBReaderViewportMonitor **文件:** `ReaderController/RDEPUBReaderViewportMonitor.swift` 视口变化监听器(旋转、尺寸变化)。 ### 10.2 RDEPUBJumpSession **文件:** `ReaderController/RDEPUBJumpSession.swift` 跳转会话管理(远距跳转时的加载状态)。 ### 10.3 RDEPUBBackgroundPriorityPolicy **文件:** `ReaderController/RDEPUBBackgroundPriorityPolicy.swift` 后台加载优先级策略。 ### 10.4 RDEPUBBackgroundCoverageStore **文件:** `ReaderController/RDEPUBBackgroundCoverageStore.swift` 后台加载覆盖范围追踪。 ### 10.5 RDEPUBPageMapReconciliationCoordinator **文件:** `ReaderController/RDEPUBPageMapReconciliationCoordinator.swift` 页图协调器(按需分页与全量分页的结果合并)。 ### 10.6 RDEpubURLReaderController **文件:** `RDEpubURLReaderController.swift` URL 阅读器控制器(用于打开单个 URL)。 ### 10.7 UIColor+RDEPUBHex **文件:** `UIColor+RDEPUBHex.swift` UIColor 十六进制扩展。 --- ## 11. 设计模式总结 | 模式 | 应用 | |------|------| | **Coordinator 模式** | 7 个独立协调器分管各功能域 | | **Context 模式** | `RDEPUBReaderContext` 共享上下文,避免循环依赖 | | **依赖注入** | `RDEPUBReaderDependencies` 工厂方法注入 | | **按需加载** | `RDEPUBChapterLoader` 章节级按需加载 | | **窗口管理** | `RDEPUBChapterWindowCoordinator` 滑动窗口驱逐 | | **快照管理** | `RDEPUBBookPageMap` 全书页图快照 | | **状态机** | `RDEPUBSelectionState` 选择状态管理 | | **磁盘缓存** | `RDEPUBChapterSummaryDiskCache` 章节摘要持久化 | --- ## 12. 数据流图 ``` 用户打开 EPUB │ ▼ RDEPUBReaderController.init(epubURL:) │ ▼ viewDidLoad() RDEPUBReaderLoadCoordinator.startInitialLoadIfNeeded() │ ├── RDEPUBParser.parse(epubURL:) → 解析 EPUB ├── RDEPUBPublication 创建 ├── 恢复阅读位置(persistence.loadLocation) │ ▼ RDEPUBReaderRuntime.applyParsedPublication() │ ├── 根据 readingProfile 选择渲染模式 │ ├── webInteractive → WebView 分页 → RDEPUBPaginator │ └── textReflowable → 文本构建 → RDEPUBTextBookBuilder │ ├── RDEPUBChapterWindowCoordinator.openBook() │ └── RDEPUBChapterLoader.loadChapter() → 按需加载 │ ├── RDEPUBBookPageMap 生成 │ └── RDEpubReaderView.transitionToPage() → 显示页面 用户翻页 │ ▼ RDEpubReaderView.currentPage 变化 │ ├── RDEPUBReaderLocationCoordinator.recordPageChangeIfNeeded() │ └── persistence.saveLocation() │ ├── RDEPUBChapterWindowCoordinator 检查是否需要加载新章节 │ └── RDEPUBReaderChromeCoordinator.updateReaderChrome() ```