- 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
501 lines
26 KiB
Markdown
501 lines
26 KiB
Markdown
# ReadViewSDK 系统架构文档
|
||
|
||
> 最后更新:2026-06-22
|
||
|
||
---
|
||
|
||
## 1. 项目概览
|
||
|
||
ReadViewSDK 是一个 iOS EPUB 阅读器 SDK,支持文本重排(Reflowable)和固定布局(Fixed Layout)两种 EPUB 格式,同时兼容纯文本 (.txt) 文件。SDK 提供完整的 EPUB 解析、文本渲染、分页计算、阅读 UI 和标注管理能力。
|
||
|
||
**技术栈:**
|
||
- 语言:Swift 5,最低 iOS 15.6
|
||
- 依赖:DTCoreText(HTML→NSAttributedString)、ZIPFoundation(EPUB 解压)、SnapKit(Auto Layout)、SSAlertSwift(弹窗)
|
||
- 构建:CocoaPods,本地 pod 引用
|
||
|
||
---
|
||
|
||
## 2. 分层架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ ReadViewDemo (Demo App) │
|
||
│ ViewController · LaunchAutomationPlan · UITests │
|
||
└──────────────────────────────┬──────────────────────────────┘
|
||
│ imports RDEpubReaderView
|
||
┌──────────────────────────────▼──────────────────────────────┐
|
||
│ EPUBUI 层 │
|
||
│ RDEPUBReaderController · Coordinators · Settings · TextPage │
|
||
│ ChapterRuntime(Loader · Store · DiskCache · PageMap) │
|
||
└──────────────────────────────┬──────────────────────────────┘
|
||
│
|
||
┌──────────────────────────────▼──────────────────────────────┐
|
||
│ RDEpubReaderView 层 │
|
||
│ RDEpubReaderView · FlowLayout · PreloadController │
|
||
│ SpreadResolver · TapRegionHandler · PagingController │
|
||
└──────────────────────────────┬──────────────────────────────┘
|
||
│
|
||
┌──────────────────────────────▼──────────────────────────────┐
|
||
│ EPUBTextRendering 层 │
|
||
│ TypesetterPipeline · DTCoreTextRenderer · Pagination │
|
||
│ BuildPipeline(TextBookBuilder · Cache · Sampler) │
|
||
└──────────────────────────────┬──────────────────────────────┘
|
||
│
|
||
┌──────────────────────────────▼──────────────────────────────┐
|
||
│ EPUBCore 层 │
|
||
│ RDEPUBParser · Publication · ResourceResolver │
|
||
│ ReadingSession · WebView · JavaScriptBridge · Paginator │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 各层职责
|
||
|
||
| 层 | 职责 | 核心类 |
|
||
|---|---|---|
|
||
| **EPUBCore** | EPUB 文件解析、资源管理、WebView 渲染、JS 桥接 | `RDEPUBParser`, `RDEPUBPublication`, `RDEPUBWebView` |
|
||
| **EPUBTextRendering** | HTML→NSAttributedString 转换、排版、分页、全书构建 | `RDEPUBTextBookBuilder`, `RDEPUBCoreTextPageFrameFactory` |
|
||
| **RDEpubReaderView** | 通用翻页容器、手势识别、页面预加载、双页布局 | `RDEpubReaderView`, `RDEpubReaderFlowLayout`, `RDEpubReaderPreloadController` |
|
||
| **EPUBUI** | 阅读器控制器、协调器模式、设置面板、按需加载、磁盘缓存 | `RDEPUBReaderController`, `RDEPUBReaderPaginationCoordinator` |
|
||
|
||
---
|
||
|
||
## 3. 核心数据流
|
||
|
||
### 3.1 打开书籍流程
|
||
|
||
```
|
||
用户点击书籍
|
||
│
|
||
▼
|
||
RDEPUBReaderController(epubURL:delegate:persistence:) // 初始化,传入 epubURL
|
||
│
|
||
├─ viewDidLoad() → startInitialLoadIfNeeded()
|
||
│ │
|
||
│ ├─ RDEPUBReaderLoadCoordinator.startInitialLoadIfNeeded()
|
||
│ │ ├─ RDEPUBParser.parse(epubURL:) // 解压 ZIP → 解析 OPF → 构建 spine/TOC
|
||
│ │ │ └─ extractArchiveIfNeeded() // ZIPFoundation 解压到 Caches
|
||
│ │ │ └─ parseContainerRootFile() // SAX 解析 container.xml
|
||
│ │ │ └─ parseOPF() // SAX 解析 OPF (metadata/manifest/spine)
|
||
│ │ │ └─ parseTOC() // NCX 或 Navigation Document
|
||
│ │ │
|
||
│ │ ├─ RDEPUBPublication(parser:) // 创建门面对象
|
||
│ │ ├─ 恢复持久化数据(书签/高亮/位置)
|
||
│ │ └─ applyParsedPublication()
|
||
│ │
|
||
│ └─ 判断 readingProfile:
|
||
│ ├─ .textReflowable → 文本排版路径(本 SDK 核心路径)
|
||
│ ├─ .webInteractive → WebView 渲染路径
|
||
│ └─ .webFixedLayout → 固定布局路径
|
||
│
|
||
└─ runtime.paginatePublication() // 进入分页流程
|
||
```
|
||
|
||
### 3.2 文本重排分页流程(大书优化路径)
|
||
|
||
```
|
||
paginateTextPublication()
|
||
│
|
||
├─ restoreBookPageMapIfPossible() // 尝试从磁盘恢复完整 BookPageMap
|
||
│ └─ summaryDiskCache.readAll(keys:) // 批量读取磁盘摘要
|
||
│ └─ 如果完整 → 直接 applyBookPageMap() → 完成
|
||
│
|
||
├─ 快速打开路径:
|
||
│ ├─ loadFirstRenderableRuntimeChapter() // 加载第一个可渲染章节
|
||
│ ├─ loadInitialRuntimeChapters() // 加载窗口内相邻章节
|
||
│ ├─ makePartialPageMap() // 构建局部 BookPageMap
|
||
│ └─ applyBookPageMap() // 应用到 UI,用户可立即阅读
|
||
│
|
||
└─ RDEPUBMetadataParseWorker.start() // 后台元数据解析
|
||
│
|
||
├─ 预计算 contentHash(串行) // 读取所有章节 HTML + SHA-256
|
||
├─ readAll(keys:) 恢复已有缓存
|
||
├─ waitForReadingInteractionToSettle() // 等待用户操作冷却 0.8s
|
||
│
|
||
├─ OperationQueue (并发 N):
|
||
│ ├─ buildChapter() // 渲染单章 (HTML→NSAttrStr→分页)
|
||
│ ├─ chapterCacheKey() // 构建缓存键(复用预计算 hash)
|
||
│ ├─ RDEPUBChapterSummary 写盘 // 异步写入磁盘摘要
|
||
│ └─ 每 32 章刷新 BookPageMap // 增量更新 UI
|
||
│
|
||
└─ 最终 applyBookPageMap() // 完整页码映射
|
||
```
|
||
|
||
### 3.3 单章渲染管线
|
||
|
||
```
|
||
Raw HTML (从 EPUB 解压目录读取)
|
||
│
|
||
▼ [RDEPUBTextTypesetterPipeline]
|
||
├─ RDEPUBHTMLNormalizer // 去 CR、合并空行、规范化附件标记
|
||
├─ RDEPUBSemanticMarkerInjector // 注入分页语义标记 ${rd-sem-start/end}
|
||
├─ RDEPUBRenderDiagnosticsCollector // 内联 <link stylesheet>、收集图片诊断
|
||
├─ RDEPUBStyleSheetComposer // 构建 5 层 CSS (default/replace/dark/epub/user)
|
||
├─ RDEPUBFontNormalizer // 注册 @font-face 嵌入字体
|
||
├─ RDEPUBFragmentMarkerInjector // 注入 fragment 锚点标记 ${id=xxx}
|
||
│
|
||
▼ [RDEPUBDTCoreTextRenderer]
|
||
├─ DTHTMLAttributedStringBuilder // HTML → NSAttributedString
|
||
├─ applyPaginationSemantics() // 将语义标记转为NSAttributedString属性
|
||
├─ extractFragmentOffsets() // 提取 fragment ID→offset 映射
|
||
├─ normalizeReadingAttributes() // 规范化字体、行距、颜色、附件
|
||
│
|
||
▼ [RDEPUBChapterPageCounter]
|
||
├─ CTFramesetter 创建帧
|
||
├─ RDEPUBPageBreakPolicy 应用语义边界规则
|
||
│ ├─ avoidPageBreakInside // 避免块内分页
|
||
│ ├─ keepWithNext // 标题与正文不分离
|
||
│ ├─ widow/orphan 控制 // 孤行/寡行控制
|
||
│ └─ pageRelate // 微信读书跨页关联
|
||
├─ RDEPUBChapterTailNormalizer // 删除/合并空白尾页
|
||
│
|
||
▼ [RDEPUBTextBookBuilder]
|
||
└─ 组装 RDEPUBTextPage[] → RDEPUBTextChapter → RDEPUBTextBook
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 缓存架构(三级缓存)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Tier 1: 内存缓存 (RDEPUBChapterRuntimeStore) │
|
||
│ ├─ chapterDataCache: [Int: RDEPUBRuntimeChapter] │
|
||
│ ├─ pageCountCache: [CacheKey: RDEPUBRuntimePageCount] │
|
||
│ ├─ imageCache: NSCache<NSString, UIImage> (50 上限) │
|
||
│ └─ 窗口驱逐:只保留当前章节 ± windowRadius 的章节 │
|
||
└──────────────────────────┬──────────────────────────────┘
|
||
│ miss
|
||
┌──────────────────────────▼──────────────────────────────┐
|
||
│ Tier 2: 磁盘摘要缓存 (RDEPUBChapterSummaryDiskCache) │
|
||
│ ├─ 路径: ~/Caches/RDEPUBChapterSummaryCache/{bookID}/ │
|
||
│ ├─ 文件名: SHA256(bookID_spineIdx_renderSig_contentHash)│
|
||
│ ├─ 格式: JSON (RDEPUBChapterSummary) │
|
||
│ ├─ 写入: 异步(serial DispatchQueue) │
|
||
│ └─ 读取: 同步(readAll 批量读取) │
|
||
└──────────────────────────┬──────────────────────────────┘
|
||
│ miss
|
||
┌──────────────────────────▼──────────────────────────────┐
|
||
│ Tier 3: 全书分页缓存 (RDEPUBTextBookCache) │
|
||
│ ├─ 路径: ~/Caches/RDEPUBTextBookCache/ │
|
||
│ ├─ 格式: NSSecureCoding archive │
|
||
│ ├─ 内容: 每章的 pageRanges + breakReasons + semanticHints│
|
||
│ └─ 失效: schemaVersion 变更时全部失效 │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 缓存键设计
|
||
|
||
```swift
|
||
struct RDEPUBChapterCacheKey: Hashable {
|
||
let bookID: String // 书籍唯一标识
|
||
let spineIndex: Int // 章节索引
|
||
let renderSignature: String // 渲染参数签名(字体/字号/行距/布局/版本)
|
||
let chapterContentHash: String // 章节 HTML 的 SHA-256
|
||
}
|
||
```
|
||
|
||
缓存键的四元组设计确保:
|
||
- 换字体/字号 → renderSignature 变化 → 缓存失效
|
||
- EPUB 内容更新 → contentHash 变化 → 缓存失效
|
||
- 不同书籍 → bookID 不同 → 互不干扰
|
||
|
||
---
|
||
|
||
## 5. ReaderController 组合架构
|
||
|
||
```
|
||
RDEPUBReaderController
|
||
│
|
||
├─ RDEPUBReaderContext(过渡门面)
|
||
│ ├─ RDEPUBReaderState
|
||
│ │ ├─ parser / publication / readingSession
|
||
│ │ ├─ textBook / bookPageMap / pendingFullPageMap
|
||
│ │ ├─ activeBookmarks / activeHighlights / searchState
|
||
│ │ └─ currentSelection / paginationToken / snapshot
|
||
│ │
|
||
│ ├─ RDEPUBReaderEnvironment
|
||
│ │ ├─ viewport / safeArea / traitCollection 抽象
|
||
│ │ ├─ brightness / fallbackViewportSize
|
||
│ │ └─ renderStyle / layoutConfig 推导
|
||
│ │
|
||
│ └─ RDEPUBReaderServices
|
||
│ ├─ parser / paginator / builder factory
|
||
│ ├─ renderer factory
|
||
│ └─ chapter summary disk cache factory
|
||
│
|
||
└─ RDEPUBReaderRuntime(兼容门面)
|
||
├─ load / pagination / location / search / chrome / annotation coordinator
|
||
├─ RDEPUBPresentationRuntime
|
||
├─ RDEPUBChapterWarmupOrchestrator
|
||
└─ 旧 API 转发
|
||
```
|
||
|
||
当前 `RDEPUBReaderContext` 仍存在,但主要作用已经收缩为过渡门面:
|
||
|
||
- 对外维持兼容访问面
|
||
- 对内把纯状态、环境推导、工厂依赖拆开
|
||
- 避免新逻辑继续把 `Context` 当作全能对象扩散
|
||
|
||
---
|
||
|
||
## 6. 章节按需加载与分页窗口架构
|
||
|
||
```
|
||
RDEPUBReaderController
|
||
│
|
||
├─ RDEPUBReaderPaginationCoordinator // 分页入口与后台元数据解析
|
||
│ ├─ paginatePublication()
|
||
│ ├─ RDEPUBMetadataParseWorker.start()
|
||
│ └─ restoreBookPageMapIfPossible()
|
||
│
|
||
├─ RDEPUBPresentationRuntime // 分页状态与窗口替换
|
||
│ ├─ applyBookPageMap()
|
||
│ ├─ refreshBookPageMapInPlace()
|
||
│ └─ applyPendingFullPageMapIfNeeded()
|
||
│
|
||
├─ RDEPUBChapterWarmupOrchestrator // 按需章节预热与边界预取
|
||
│ ├─ prepareOnDemandChapter()
|
||
│ ├─ extendPartialBookPageMapIfNeeded()
|
||
│ ├─ prefetchForwardChaptersAfterInitialOpen()
|
||
│ └─ ensureNavigationTargetAvailable()
|
||
│
|
||
├─ RDEPUBChapterLoader // 章节构建与缓存门面
|
||
│ ├─ loadChapter() // 异步加载(主路径)
|
||
│ └─ loadChapterSynchronouslyForMigration()
|
||
│
|
||
└─ RDEPUBBookPageMap / RDEPUBPaginationState
|
||
├─ 当前激活窗口
|
||
├─ 待接管完整 page map
|
||
└─ 来源与切换状态
|
||
```
|
||
|
||
这轮整改后,跨章节主路径的关键变化是:
|
||
|
||
- 普通翻页时不再要求 UI 主线程同步等待章节构建
|
||
- 章节边界会前移预热相邻章节与 lookahead 章节
|
||
- `bookPageMap` 的 partial extension 和 full replacement 统一经过 `RDEPUBPresentationRuntime`
|
||
- `RDEPUBReaderRuntime` 不再内联维护整套预热/扩窗实现
|
||
|
||
---
|
||
|
||
## 6. WebView 渲染架构(Web 路径)
|
||
|
||
```
|
||
RDEPUBWebView (UIView)
|
||
│
|
||
├─ WKWebView (RDEPUBAnnotationWebView)
|
||
│ ├─ 自定义选择菜单(拷贝/高亮/批注)
|
||
│ └─ 禁用系统手势(滚动手势由原生控制)
|
||
│
|
||
├─ RDEPUBResourceURLSchemeHandler
|
||
│ ├─ 协议: ss-reader://book/<path>
|
||
│ ├─ 映射: URL → 本地 EPUB 解压文件
|
||
│ └─ 容错: 缺失资源返回空响应(不 404)
|
||
│
|
||
├─ JavaScript Bridge (6 种消息)
|
||
│ ├─ ssReaderProgressionChanged // 阅读进度更新
|
||
│ ├─ ssReaderSelectionChanged // 文本选区变化
|
||
│ ├─ ssReaderInternalLink // EPUB 内部链接
|
||
│ ├─ ssReaderExternalLink // 外部链接
|
||
│ ├─ ssReaderJSError // JS 运行时错误
|
||
│ └─ ssReaderFixedLayoutReady // 固定布局加载完成
|
||
│
|
||
└─ 注入脚本
|
||
├─ epub-bridge.js (502 行) // 核心桥接,分页/滚动/选区/高亮
|
||
├─ cssInjector.js (45 行) // CSS 注入管理
|
||
├─ WeReadApi.js (67 行) // 微信读书 API 兼容层
|
||
├─ rangy-core.js / rangy-serializer.js // DOM Range 序列化
|
||
└─ CSS: default/replace/dark/latin // 4 套样式表
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 翻页容器架构(RDEpubReaderView)
|
||
|
||
```
|
||
RDEpubReaderView (UIView)
|
||
│
|
||
├─ 三种翻页模式:
|
||
│ ├─ .pageCurl → UIPageViewController (翻页动画)
|
||
│ ├─ .horizontalScroll → UICollectionView (水平滑动)
|
||
│ └─ .verticalScroll → UICollectionView (垂直滚动)
|
||
│
|
||
├─ 组合对象:
|
||
│ ├─ RDEpubReaderPagingController // 翻页状态管理、请求队列
|
||
│ ├─ RDEpubReaderPreloadController // 页面预加载、缓存管理
|
||
│ ├─ RDEpubReaderSpreadResolver // 双页展开计算
|
||
│ └─ RDEpubReaderTapRegionHandler // 点击区域分类(左/中/右)
|
||
│
|
||
├─ 数据源协议:
|
||
│ ├─ RDEpubReaderPageProvider (新) // 格式无关,优先级高
|
||
│ └─ RDEpubReaderDataSource (旧) // 遗留兼容,通过 Adapter 适配
|
||
│
|
||
└─ 手势流:
|
||
点击 → TapRegionHandler → 分类(左/中/右)
|
||
├─ 左 → goPreviousPage()
|
||
├─ 右 → goNextPage()
|
||
└─ 中 → tapCenter() (切换工具栏)
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 配置与设置
|
||
|
||
### RDEPUBReaderConfiguration
|
||
|
||
```swift
|
||
struct RDEPUBReaderConfiguration {
|
||
var fontSize: CGFloat // 默认 16
|
||
var fontChoice: FontChoice // 系统/宋体/楷体
|
||
var lineHeightMultiple: CGFloat // 默认 1.5
|
||
var numberOfColumns: Int // 1 或 2
|
||
var columnGap: CGFloat // 默认 20
|
||
var theme: ReaderTheme // 6 种主题
|
||
var displayType: RDEpubReaderView.DisplayType // pageCurl/horizontal/vertical
|
||
var onDemandChapterWindowSize: Int // 默认 3(奇数,最小 3,最大 15)
|
||
var metadataParsingConcurrency: Int // 默认 CPU 核心数
|
||
var chapterWindowRadius: Int // 内存缓存窗口半径
|
||
}
|
||
```
|
||
|
||
### 持久化
|
||
|
||
| 数据 | 存储方式 | Key 前缀 |
|
||
|------|----------|----------|
|
||
| 阅读位置 | UserDefaults | `ssreader.epub.location.{bookID}` |
|
||
| 书签 | UserDefaults | `ssreader.epub.bookmarks.{bookID}` |
|
||
| 高亮标注 | UserDefaults | `ssreader.epub.highlights.{bookID}` |
|
||
| 全局设置 | UserDefaults | `ssreader.epub.settings` |
|
||
| 章节摘要 | 磁盘文件 | `~/Caches/RDEPUBChapterSummaryCache/` |
|
||
| 全书分页 | 磁盘文件 | `~/Caches/RDEPUBTextBookCache/` |
|
||
|
||
---
|
||
|
||
## 9. 目录结构
|
||
|
||
```
|
||
Sources/RDEpubReaderView/
|
||
├── EPUBCore/ # EPUB 解析与 WebView 渲染
|
||
│ ├── Models/ # 数据模型
|
||
│ │ ├── RDEPUBAnnotationModels.swift
|
||
│ │ ├── RDEPUBPaginationModels.swift
|
||
│ │ └── RDEPUBReadingLocationModels.swift
|
||
│ ├── Resources/ # JS/CSS/HTML 静态资源
|
||
│ │ ├── epub-bridge.js
|
||
│ │ ├── cssInjector.js
|
||
│ │ ├── WeReadApi.js
|
||
│ │ ├── rangy-core.js / rangy-serializer.js
|
||
│ │ ├── wxread-default.css / wxread-dark.css / wxread-replace-latin.css
|
||
│ │ └── epub-fixed-layout.html
|
||
│ ├── RDEPUBParser.swift # 核心解析器
|
||
│ ├── RDEPUBParser+Archive.swift # ZIP 解压
|
||
│ ├── RDEPUBParser+Package.swift # OPF 解析
|
||
│ ├── RDEPUBParser+TOC.swift # 目录解析
|
||
│ ├── RDEPUBParser+Resources.swift # 资源访问
|
||
│ ├── RDEPUBPublication.swift # 出版物门面
|
||
│ ├── RDEPUBResourceResolver.swift # URL 解析
|
||
│ ├── RDEPUBReadingSession.swift # 阅读会话状态机
|
||
│ ├── RDEPUBWebView.swift # WebView 主类
|
||
│ ├── RDEPUBWebView+*.swift # WebView 扩展(配置/重排/固定/搜索/JS桥接)
|
||
│ ├── RDEPUBPaginator.swift # 离屏分页计算器
|
||
│ ├── RDEPUBSearchEngine.swift # 全文搜索引擎
|
||
│ └── ...
|
||
│
|
||
├── EPUBTextRendering/ # 文本渲染引擎
|
||
│ ├── Typesetter/ # 排版管线
|
||
│ │ ├── RDEPUBTypesettingPipeline.swift
|
||
│ │ ├── RDEPUBHTMLNormalizer.swift
|
||
│ │ ├── RDEPUBSemanticMarkerInjector.swift
|
||
│ │ ├── RDEPUBFragmentMarkerInjector.swift
|
||
│ │ ├── RDEPUBFontNormalizer.swift
|
||
│ │ ├── RDEPUBAttachmentNormalizer.swift
|
||
│ │ ├── RDEPUBStyleSheetComposer.swift
|
||
│ │ ├── RDEPUBRenderDiagnosticsCollector.swift
|
||
│ │ └── RDEPUBTextRendererSupport.swift
|
||
│ ├── Pagination/ # 分页引擎
|
||
│ │ ├── RDEPUBChapterPageCounter.swift
|
||
│ │ ├── RDEPUBCoreTextPageFrameFactory.swift
|
||
│ │ ├── RDEPUBPageBreakPolicy.swift
|
||
│ │ ├── RDEPUBTextLayoutFrame.swift
|
||
│ │ └── ...
|
||
│ ├── BuildPipeline/ # 全书构建
|
||
│ │ ├── RDEPUBTextBookBuilder.swift
|
||
│ │ ├── RDEPUBTextBookModels.swift
|
||
│ │ ├── RDEPUBTextBookCache.swift
|
||
│ │ ├── RDEPUBChapterTailNormalizer.swift
|
||
│ │ └── ...
|
||
│ ├── RDEPUBTextRenderer.swift # 渲染器协议与类型定义
|
||
│ ├── RDEPUBDTCoreTextRenderer.swift # DTCoreText 渲染器实现
|
||
│ ├── RDEPUBChapterData.swift # 章节查询门面
|
||
│ ├── RDEPUBTextIndexTable.swift # 全书索引表
|
||
│ └── RDEpubPlainTextBookBuilder.swift # 纯文本 (.txt) 构建器
|
||
│
|
||
├── ReaderView/ # 通用翻页容器
|
||
│ ├── Paging/ # 翻页子系统
|
||
│ │ ├── RDEpubReaderPagingController.swift
|
||
│ │ ├── RDEpubReaderPreloadController.swift
|
||
│ │ ├── RDEpubReaderSpreadResolver.swift
|
||
│ │ └── RDEpubReaderTapRegionHandler.swift
|
||
│ ├── RDEpubReaderView.swift # 主容器视图
|
||
│ ├── RDEpubReaderView+*.swift # 扩展(CollectionView/PageCurl/ToolView/ContentAccess)
|
||
│ ├── RDEpubReaderFlowLayout.swift # 自定义 CollectionView 布局
|
||
│ ├── RDEpubReaderContentCell.swift # 滚动模式 Cell
|
||
│ └── RDEpubReaderPageChildViewController.swift // 翻页模式子 VC
|
||
│
|
||
└── EPUBUI/ # 阅读器 UI
|
||
├── ReaderController/ # 控制器与协调器
|
||
│ ├── ChapterRuntime/ # 章节运行时
|
||
│ │ ├── RDEPUBChapterLoader.swift
|
||
│ │ ├── RDEPUBChapterRuntimeStore.swift
|
||
│ │ ├── RDEPUBChapterSummaryDiskCache.swift
|
||
│ │ ├── RDEPUBBookPageMap.swift
|
||
│ │ ├── RDEPUBPageResolver.swift
|
||
│ │ └── ...
|
||
│ ├── RDEPUBReaderContext.swift // 共享状态中心
|
||
│ ├── RDEPUBReaderRuntime.swift // 运行时协调器(Facade)
|
||
│ ├── RDEPUBReaderDependencies.swift // 依赖注入
|
||
│ ├── RDEPUBReaderLoadCoordinator.swift // 加载协调器
|
||
│ ├── RDEPUBReaderPaginationCoordinator.swift // 分页协调器
|
||
│ ├── RDEPUBReaderLocationCoordinator.swift // 位置协调器
|
||
│ ├── RDEPUBReaderSearchCoordinator.swift // 搜索协调器
|
||
│ ├── RDEPUBReaderChromeCoordinator.swift // 工具栏协调器
|
||
│ ├── RDEPUBReaderAnnotationCoordinator.swift // 标注协调器
|
||
│ ├── RDEPUBReaderAssemblyCoordinator.swift // 组装协调器
|
||
│ ├── RDEPUBReaderViewportMonitor.swift // 视口监控
|
||
│ └── ...
|
||
├── Settings/ # 设置面板
|
||
│ ├── RDEPUBReaderConfiguration.swift # 配置模型
|
||
│ ├── RDEPUBReaderSettings.swift # 持久化设置
|
||
│ ├── RDEPUBReaderSettingsViewController.swift # 设置面板 VC
|
||
│ ├── RDEPUBReaderTheme.swift # 主题定义
|
||
│ └── ...
|
||
├── TextPage/ # 文本页面渲染
|
||
│ ├── RDEPUBTextContentView.swift # 文本内容视图(CoreText)
|
||
│ ├── RDEPUBTextSelectionController.swift # 文本选择控制器
|
||
│ ├── RDEPUBPageInteractionController.swift # 点击交互控制器
|
||
│ ├── RDEPUBSelectionOverlayView.swift # 选区覆盖层
|
||
│ ├── RDEPUBTextAnnotationOverlay.swift # 标注覆盖层
|
||
│ ├── RDEPUBTextPageRenderView.swift # CoreText 绘制视图
|
||
│ └── ...
|
||
├── RDEPUBReaderController.swift # 主控制器(+ContentDelegates/DataSource/PublicAPI 等扩展)
|
||
├── RDEPUBReaderDelegate.swift # 公开委托协议
|
||
├── RDEPUBReaderPersistence.swift # 持久化协议
|
||
└── ...
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 关键设计模式
|
||
|
||
| 模式 | 应用场景 |
|
||
|------|----------|
|
||
| **Coordinator** | 分页/标注/搜索/导航各一个协调器,解耦 Controller |
|
||
| **Facade** | `RDEPUBPublication` 封装 `RDEPUBParser`,`RDEPUBChapterData` 封装章节查询 |
|
||
| **Builder** | `RDEPUBBookPageMap.Builder` 增量构建页码映射 |
|
||
| **Strategy** | `RDEPUBTextRenderer` 协议,可替换渲染器实现 |
|
||
| **Pipeline** | `RDEPUBTextTypesetterPipeline` 排版管线(8 个逻辑阶段,封装为 5-6 个顶层调用) |
|
||
| **Adapter** | `RDEpubReaderLegacyDataSourceAdapter` 适配旧数据源协议 |
|
||
| **三级缓存** | 内存 → 磁盘摘要 → 全书分页,逐级降级 |
|
||
| **Token 取消** | `paginationToken` 确保过期异步任务不干扰新任务 |
|
||
| **Frozen Parameters** | 后台任务冻结 `renderSignature`,避免运行中参数漂移 |
|