ReadViewSDK/Doc/EPUBUI_CODE_REFERENCE.md
shenlei d7fcda345d refactor: rename RDReaderView -> RDEpubReaderView, update pod config and docs
- Rename source module from RDReaderView to RDEpubReaderView
- Move all source files from Sources/RDReaderView/ to Sources/RDEpubReaderView/
- Update podspec: RDReaderView.podspec -> RDEpubReaderView.podspec
- Update Podfile, demo project, and CocoaPods config for new pod name
- Delete old RDReaderView pod support files from ReadViewDemo/Pods
- Add new RDEpubReaderView pod support files
- Update documentation (API ref, architecture, UML, conventions, etc.)
- Add FixedLayoutRotationTests
- Update .gitignore: exclude .DS_Store, manual unpack backups, _ssoft-output
2026-07-10 19:44:53 +09:00

23 KiB
Raw Permalink Blame History

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 子类,是整个阅读器的入口。

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

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 实现。

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 引用访问。

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

依赖注入容器,支持测试替身。

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

运行时管理器,持有所有协调器和子系统。

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 → 恢复阅读位置。

final class RDEPUBReaderLoadCoordinator {
    func startInitialLoadIfNeeded()  // 开始初始加载
    func loadPublication()            // 解析 EPUB 文件
    func applyParsedPublication(...)  // 应用解析结果
}

4.2 RDEPUBReaderPaginationCoordinator

文件: ReaderController/RDEPUBReaderPaginationCoordinator.swift

负责分页调度WebView 分页 → 文本构建 → 页图生成。

final class RDEPUBReaderPaginationCoordinator {
    func startPagination(...)         // 开始分页
    func applyPageCounts(...)         // 应用页数结果
    func repaginatePreservingCurrentLocation()  // 重新分页(保持位置)
}

4.3 RDEPUBReaderLocationCoordinator

文件: ReaderController/RDEPUBReaderLocationCoordinator.swift

负责位置管理:恢复位置、记录位置变化、目录跳转。

final class RDEPUBReaderLocationCoordinator {
    func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool, ...) -> Bool
    func currentVisibleLocation() -> RDEPUBLocation?
    func recordPageChangeIfNeeded()
}

4.4 RDEPUBReaderSearchCoordinator

文件: ReaderController/RDEPUBReaderSearchCoordinator.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工具栏、搜索栏的创建和状态更新。

final class RDEPUBReaderChromeCoordinator {
    func makeTopToolView() -> RDEPUBReaderTopToolView
    func makeBottomToolView() -> RDEPUBReaderBottomToolView
    func updateReaderChrome()
    func toggleSearchBar()
    func presentTableOfContents()
    func presentSettings()
}

4.6 RDEPUBReaderAnnotationCoordinator

文件: ReaderController/RDEPUBReaderAnnotationCoordinator.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

章节数据的核心存储,管理内存缓存和窗口。

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

章节按需加载器,支持优先级和磁盘摘要缓存。

final class RDEPUBChapterLoader {
    enum LoadPriority {
        case navigation   // 导航(最高优先级)
        case preview      // 预览
        case prefetch     // 预取(最低优先级)
    }

    func loadChapter(
        spineIndex: Int,
        store: RDEPUBChapterRuntimeStore,
        priority: LoadPriority,
        completion: @escaping (Result<RDEPUBRuntimeChapter, Error>) -> Void
    )
}

加载流程:

  1. 检查内存缓存(RDEPUBChapterDataCache
  2. 检查页数缓存(RDEPUBPageCountCache
  3. 检查磁盘摘要缓存(RDEPUBChapterSummaryDiskCache
  4. 构建章节(排版 + 分页)
  5. 写入缓存

5.3 RDEPUBChapterWindowCoordinator

文件: ChapterRuntime/RDEPUBChapterWindowCoordinator.swift

管理章节窗口(当前章 + 前后各 N 章),协调加载和驱逐。

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 索引 + 本地页码。

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

将绝对页码解析为具体的页面数据。

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

阅读器配置模型。

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

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

enum RDEPUBSelectionState: Equatable {
    case idle                                    // 无选择
    case selecting(anchor: Int)                  // 选择中
    case selected(RDEPUBSelection)               // 已选择
    case committingAction(RDEPUBSelection, action: RDEPUBAnnotationMenuAction)  // 执行操作中
}

9.2 RDEPUBReaderUIState

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()