// RDEPUBReaderController+RuntimeBridge.swift // EPUB 阅读器运行时桥接层 // 将阅读器控制器的公开行为委托给 runtime 对象执行,涵盖配置应用、 // 出版物加载与分页、阅读位置恢复、加载状态管理、错误处理等生命周期操作。 import UIKit /// RDEPUBReaderController 运行时桥接扩展 /// /// 本文件将阅读器控制器的公开行为委托给 runtime 对象执行,涵盖配置应用、 /// 出版物加载与分页、阅读位置恢复、加载状态管理、错误处理等生命周期操作。 extension RDEPUBReaderController { /// 应用阅读器视图配置,处理显示类型/翻页方向/横屏双页等变更并恢复位置 func applyReaderViewConfiguration() { let resolvedDirection = resolvedPageDirection() let presentationDidChange = readerView.currentDisplayType != configuration.displayType || readerView.landscapeDualPageEnabled != configuration.landscapeDualPageEnabled || readerView.pageDirection != resolvedDirection let preservedLocation = presentationDidChange ? (runtime.viewportMonitor.consumePendingPresentationRestoreLocation() ?? currentVisibleLocation() ?? persistenceLocation()) : nil view.backgroundColor = configuration.theme.contentBackgroundColor readerView.landscapeDualPageEnabled = configuration.landscapeDualPageEnabled readerView.pageDirection = resolvedDirection updateReaderChrome() if presentationDidChange { readerView.switchReaderDisplayType(configuration.displayType) if let preservedLocation { _ = restoreReadingLocation(preservedLocation) } } } /// 启动初始加载流程(如果尚未加载) func startInitialLoadIfNeeded() { runtime.startInitialLoadIfNeeded() } /// 加载 EPUB 出版物 func loadPublication() { runtime.loadPublication() } /// 应用已解析的出版物数据(解析器、出版物模型、书签、高亮等) func applyParsedPublication( parser: RDEPUBParser, publication: RDEPUBPublication, bookIdentifier: String, restoreLocation: RDEPUBLocation?, bookmarks: [RDEPUBBookmark], highlights: [RDEPUBHighlight] ) { runtime.applyParsedPublication( parser: parser, publication: publication, bookIdentifier: bookIdentifier, restoreLocation: restoreLocation, bookmarks: bookmarks, highlights: highlights ) } /// 对出版物执行分页计算 func paginatePublication(restoreLocation: RDEPUBLocation?) { runtime.paginatePublication(restoreLocation: restoreLocation) } /// 应用文本书籍数据并恢复阅读位置 func applyTextBook(_ textBook: RDEPUBTextBook, restoreLocation: RDEPUBLocation?) { runtime.applyTextBook(textBook, restoreLocation: restoreLocation) } /// 应用分页快照(页面列表与章节信息) func applyPaginationSnapshot( _ snapshot: (pages: [EPUBPage], chapters: [EPUBChapterInfo]), restoreLocation: RDEPUBLocation? ) { runtime.applyPaginationSnapshot(snapshot, restoreLocation: restoreLocation) } /// 完成分页流程并恢复阅读位置 func finishPagination(restoreLocation: RDEPUBLocation?) { runtime.finishPagination(restoreLocation: restoreLocation) } /// 保留当前位置重新执行分页 func repaginatePreservingCurrentLocation() { runtime.repaginatePreservingCurrentLocation() } /// 恢复到指定阅读位置,返回是否成功 @discardableResult func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool = false) -> Bool { runtime.restoreReadingLocation(location, animated: animated) } /// 获取当前可见页面的位置 func currentVisibleLocation() -> RDEPUBLocation? { runtime.currentVisibleLocation() } /// 获取持久化存储的阅读位置 func persistenceLocation() -> RDEPUBLocation? { readerContext.persistenceLocation() } /// 持久化当前阅读位置 func persist(location: RDEPUBLocation) { readerContext.persist(location: location) } /// 更新当前文本选区状态 func updateCurrentSelection(_ selection: RDEPUBSelection?) { runtime.annotationCoordinator.updateCurrentSelection(selection) } /// 将选区限定到指定 spine 范围内 func scopedSelection( _ selection: RDEPUBSelection, relativeToSpineIndex spineIndex: Int? ) -> RDEPUBSelection? { runtime.annotationCoordinator.scopedSelection(selection, relativeToSpineIndex: spineIndex) } /// 刷新可见内容并保留当前位置 func refreshVisibleContentPreservingLocation() { runtime.refreshVisibleContentPreservingLocation() } /// 重建外部文本书籍 func rebuildExternalTextBook() { runtime.rebuildExternalTextBook() } /// 更新阅读器 UI 装饰层(导航栏、工具栏等) func updateReaderChrome() { runtime.updateReaderChrome() } /// 展示书签管理界面 func presentBookmarksManager() { runtime.presentBookmarksManager() } /// 展示高亮标注管理界面 func presentHighlightsManager() { runtime.presentHighlightsManager() } /// 展示标注创建界面 func presentAnnotationCreation() { runtime.presentAnnotationCreation() } /// 处理选区菜单操作(复制、标注、分享等) func handleSelectionMenuAction(_ action: RDEPUBAnnotationMenuAction, selection: RDEPUBSelection?) { runtime.handleSelectionMenuAction(action, selection: selection) } /// 展示阅读设置界面 func presentSettings() { runtime.presentSettings() } /// 使用闭包更新阅读器配置 func updateConfiguration(_ update: (inout RDEPUBReaderConfiguration) -> Void) { var nextConfiguration = configuration update(&nextConfiguration) configuration = nextConfiguration } /// 设置屏幕亮度并持久化 func setScreenBrightness(_ brightness: CGFloat) { currentBrightness = max(0, min(1, brightness)) persistReaderSettingsIfNeeded() } /// 持久化阅读器设置(亮度、配置等) func persistReaderSettingsIfNeeded() { let settings = RDEPUBReaderSettings.capture( configuration: configuration, brightness: currentBrightness ) persistence?.saveReaderSettings(settings) } /// 判断配置变更是否需要重新分页 func requiresRepagination( from oldConfiguration: RDEPUBReaderConfiguration, to newConfiguration: RDEPUBReaderConfiguration ) -> Bool { oldConfiguration.fontSize != newConfiguration.fontSize || oldConfiguration.fontChoice != newConfiguration.fontChoice || oldConfiguration.lineHeightMultiple != newConfiguration.lineHeightMultiple || oldConfiguration.numberOfColumns != newConfiguration.numberOfColumns || oldConfiguration.columnGap != newConfiguration.columnGap || oldConfiguration.reflowableContentInsets != newConfiguration.reflowableContentInsets || oldConfiguration.fixedContentInset != newConfiguration.fixedContentInset || oldConfiguration.fixedLayoutFit != newConfiguration.fixedLayoutFit || oldConfiguration.fixedLayoutSpreadMode != newConfiguration.fixedLayoutSpreadMode || oldConfiguration.textRenderingEngine != newConfiguration.textRenderingEngine } /// 判断配置变更是否需要刷新可见内容 func requiresVisibleRefresh( from oldConfiguration: RDEPUBReaderConfiguration, to newConfiguration: RDEPUBReaderConfiguration ) -> Bool { oldConfiguration.theme != newConfiguration.theme || oldConfiguration.darkImageAdjustmentEnabled != newConfiguration.darkImageAdjustmentEnabled || oldConfiguration.darkImageBlendRatio != newConfiguration.darkImageBlendRatio } /// 展示目录界面 func presentTableOfContents() { runtime.presentTableOfContents() } /// 处理返回操作 func handleBackAction() { runtime.handleBackAction() } /// 处理错误:停止分页、隐藏加载指示器、显示错误信息并通知代理 func handle(error: Error) { isRepaginating = false hideLoading() readerContext.clearActiveSnapshot() textBook = nil readerContext.bookPageMap = nil readerView.reloadData() errorLabel.text = error.localizedDescription errorLabel.isHidden = false delegate?.epubReader(self, didFailWithError: error) } /// 显示加载指示器 func showLoading() { errorLabel.isHidden = true loadingIndicator.startAnimating() } /// 隐藏加载指示器 func hideLoading() { loadingIndicator.stopAnimating() } /// 获取当前视口签名(用于检测视口变化) func currentViewportSignature() -> RDEPUBViewportSignature? { runtime.currentViewportSignature() } /// 检测并处理视口变化(旋转、尺寸变化等) func handleViewportChangeIfNeeded( reason: RDEPUBViewportChangeReason, viewportSignature: RDEPUBViewportSignature? = nil ) { runtime.handleViewportChangeIfNeeded(reason: reason, viewportSignature: viewportSignature) } /// 获取指定页面的搜索结果展示状态 func searchPresentation(for page: EPUBPage) -> RDEPUBSearchPresentation? { runtime.searchPresentation(for: page) } /// 根据出版物阅读方向解析页面翻页方向 private func resolvedPageDirection() -> RDReaderView.PageDirection { publication?.readingProgression == .rtl ? .rightToLeft : .leftToRight } } // MARK: - 手势识别器代理 extension RDEPUBReaderController: UIGestureRecognizerDelegate { /// 手势识别器代理,始终返回 true 允许手势触发 public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { true } }