// RDEPUBReaderController+DataSource.swift // EPUB 阅读器数据源与代理实现 // 实现 RDReaderDataSource 和 RDReaderDelegate 协议,为阅读器视图提供页面数量、 // 页面内容视图、页码变化通知、屏幕方向变化处理等核心数据与事件支持。 import UIKit /// RDEPUBReaderController 数据源与代理扩展 /// /// 本文件实现 RDReaderDataSource 和 RDReaderDelegate 协议,为阅读器视图提供页面数量、 /// 页面内容视图、页码变化通知、屏幕方向变化处理等核心数据与事件支持。 // MARK: - RDReaderView 数据源与代理 extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate { /// 返回阅读器总页数 public func pageCountOfReaderView(readerView: RDReaderView) -> Int { readerContext.bookPageMap?.totalPages ?? textBook?.pages.count ?? activePages.count } /// 为指定页码创建或复用内容视图(优先文本渲染,回退 Web 渲染) public func pageContentView(readerView: RDReaderView, pageNum: Int, containerView: UIView?) -> UIView { if readerContext.bookPageMap != nil { _ = runtime.prepareOnDemandChapter(forAbsolutePageNumber: pageNum + 1) if let resolvedPage = runtime.pageResolver.resolvePage(absolutePageIndex: pageNum) { let contentView = (containerView as? RDEPUBTextContentView) ?? RDEPUBTextContentView() contentView.delegate = self contentView.configure( page: resolvedPage.page, pageNumber: pageNum + 1, totalPages: pageCountOfReaderView(readerView: readerView), configuration: configuration, highlights: textHighlights(for: resolvedPage.page), searchState: searchState ) return contentView } } 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 && readerContext.bookPageMap == 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) { readerContext.markUserNavigationActivity() updateCurrentSelection(nil) reconcileTextPaginationSizeIfNeeded(for: pageNum) // 用户开始导航时,应用后台解析完成的完整 map runtime.applyPendingFullPageMapIfNeeded() if readerContext.bookPageMap != nil { _ = runtime.prepareOnDemandChapter(forAbsolutePageNumber: pageNum + 1) runtime.extendPartialBookPageMapIfNeeded(currentPageNumber: pageNum + 1) } let totalPages = pageCountOfReaderView(readerView: readerView) if totalPages > 0, pageNum == totalPages - 1 { delegate?.epubReaderDidReachEnd(self) } if (textBook != nil || readerContext.bookPageMap != 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 || readerContext.bookPageMap != 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.readerContext.bookPageMap != nil), !self.isRepaginating else { return } self.repaginatePreservingCurrentLocation() } } }