Files
ReadViewSDK/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+DataSource.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

154 lines
5.9 KiB
Swift

// RDEPUBReaderController+DataSource.swift
// EPUB 阅读器数据源与代理实现
// 实现 RDReaderDataSource 和 RDReaderDelegate 协议,为阅读器视图提供页面数量、
// 页面内容视图、页码变化通知、屏幕方向变化处理等核心数据与事件支持。
import UIKit
/// RDEPUBReaderController 数据源与代理扩展
///
/// 本文件实现 RDReaderDataSource 和 RDReaderDelegate 协议,为阅读器视图提供页面数量、
/// 页面内容视图、页码变化通知、屏幕方向变化处理等核心数据与事件支持。
// MARK: - RDReaderView 数据源与代理
extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
/// 返回阅读器总页数
public func pageCountOfReaderView(readerView: RDReaderView) -> Int {
textBook?.pages.count ?? activePages.count
}
/// 为指定页码创建或复用内容视图(优先文本渲染,回退 Web 渲染)
public func pageContentView(readerView: RDReaderView, pageNum: Int, containerView: UIView?) -> UIView {
if let textBook, let page = textBook.page(at: pageNum + 1) {
let contentView = (containerView as? RDEPUBTextContentView) ?? RDEPUBTextContentView()
contentView.delegate = self
contentView.configure(
page: page,
pageNumber: pageNum + 1,
totalPages: textBook.pages.count,
configuration: configuration,
highlights: textHighlights(for: page),
searchState: searchState
)
return contentView
}
guard let publication,
let request = request(for: pageNum) else {
return containerView ?? UIView()
}
let contentView = (containerView as? RDEPUBWebContentView) ?? RDEPUBWebContentView()
contentView.delegate = self
contentView.configure(
publication: publication,
request: request,
pageNumber: pageNum + 1,
totalPages: activePages.count,
theme: configuration.theme
)
return contentView
}
/// 返回页面内容视图的重用标识符(区分文本与 Web 渲染)
public func pageIdentifier(readerView: RDReaderView, pageNum: Int) -> String? {
textBook == nil
? NSStringFromClass(RDEPUBWebContentView.self)
: NSStringFromClass(RDEPUBTextContentView.self)
}
/// 获取指定文本页面上的高亮标注列表
private func textHighlights(for page: RDEPUBTextPage) -> [RDEPUBHighlight] {
if let textBook,
let chapterData = textBook.chapterData(for: page.href) {
return chapterData.highlights(on: page, from: activeHighlights)
}
guard let publication else {
return activeHighlights.filter { $0.location.href == page.href }
}
let pageHref = publication.resourceResolver.normalizedHref(page.href) ?? page.href
return activeHighlights.filter {
(publication.resourceResolver.normalizedHref($0.location.href) ?? $0.location.href) == pageHref
}
}
/// 根据规范化 href 获取文本章节数据
func textChapterData(forNormalizedHref href: String) -> RDEPUBChapterData? {
readerContext.textChapterData(forNormalizedHref: href)
}
/// 返回阅读器顶部工具视图
public func topToolView(readerView: RDReaderView) -> UIView? {
topToolView
}
/// 返回阅读器底部工具视图
public func bottomToolView(readerView: RDReaderView) -> UIView? {
bottomToolView
}
/// 页码变化回调:清除选区、同步阅读状态、检测到达末尾
public func pageNum(readerView: RDReaderView, pageNum: Int) {
updateCurrentSelection(nil)
reconcileTextPaginationSizeIfNeeded(for: pageNum)
let totalPages = pageCountOfReaderView(readerView: readerView)
if totalPages > 0, pageNum == totalPages - 1 {
delegate?.epubReaderDidReachEnd(self)
}
if textBook != nil,
let location = resolvedTextLocation(forPageNumber: pageNum + 1) {
persist(location: location)
synchronizeTextReadingState(pageNumber: pageNum + 1, location: location)
return
}
if let location = fallbackLocation(for: pageNum) {
persist(location: location)
}
if readingSession?.navigatorState == .jumping || readingSession?.navigatorState == .moving {
readingSession?.transition(to: .idle)
}
}
/// 屏幕方向即将变化回调,捕获待恢复的展示位置
public func readerViewOrientationWillChange(readerView: RDReaderView, isLandscape: Bool) {
_ = isLandscape
runtime.viewportMonitor.capturePendingPresentationRestoreLocation()
}
/// 检测页面尺寸变化并触发重新分页(避免布局错乱)
private func reconcileTextPaginationSizeIfNeeded(for pageNum: Int) {
guard textBook != nil,
!isRepaginating,
!isReconcilingTextPaginationSize,
pageNum >= 0,
let lastTextPaginationPageSize else {
return
}
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
guard resolvedSize.width > 0,
resolvedSize.height > 0 else {
return
}
let sizeChanged = abs(resolvedSize.width - lastTextPaginationPageSize.width) > 0.5
|| abs(resolvedSize.height - lastTextPaginationPageSize.height) > 0.5
guard sizeChanged else {
return
}
isReconcilingTextPaginationSize = true
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.isReconcilingTextPaginationSize = false
guard self.textBook != nil, !self.isRepaginating else { return }
self.repaginatePreservingCurrentLocation()
}
}
}