import UIKit /// UIPageViewController 数据源和代理实现 /// 处理仿真翻页模式下的页面数据供给和翻页事件 extension RDReaderView: UIPageViewControllerDataSource, UIPageViewControllerDelegate { /// 创建指定页码的子控制器 /// 空白页返回空视图,正常页从缓存或数据源获取内容 private func makeSinglePageChildVC(for pageNum: Int) -> RDReaderPageChildViewController { if pageNum == RDReaderView.blankPageNum || pageNum == RDReaderView.blankEndPageNum { return RDReaderPageChildViewController(contentView: UIView(), pageNum: pageNum) } let contentView = pageViewForDisplay(pageNum: pageNum) return RDReaderPageChildViewController(contentView: contentView, pageNum: pageNum) } /// 计算某页的"下一页"页码 /// 考虑封面页独占和末尾空白页配对的情况 private func nextPageNum(after pageNum: Int, isDualPage: Bool) -> Int? { let totalPages = numberOfPages() // 末尾空白页之后没有更多页 if pageNum == RDReaderView.blankEndPageNum { return nil } if isDualPage, let coverIndex = coverPageIndex { if pageNum == coverIndex { return RDReaderView.blankPageNum } if pageNum == RDReaderView.blankPageNum { let firstContent = coverIndex + 1 return firstContent < totalPages ? firstContent : nil } } let next = pageNum + 1 if next < totalPages { return next } // pageNum 是最后一页,检查在双页模式下是否需要空白页配对 if isDualPage { if let coverIndex = coverPageIndex { let adjustedIndex = pageNum - (coverIndex + 1) if adjustedIndex >= 0 && adjustedIndex % 2 == 0 { return RDReaderView.blankEndPageNum } } else { if pageNum % 2 == 0 { return RDReaderView.blankEndPageNum } } } return nil } /// 计算某页的"上一页"页码 /// 考虑封面页独占和末尾空白页配对的情况 private func prevPageNum(before pageNum: Int, isDualPage: Bool) -> Int? { if pageNum == RDReaderView.blankEndPageNum { let totalPages = numberOfPages() return totalPages > 0 ? totalPages - 1 : nil } if isDualPage, let coverIndex = coverPageIndex { if pageNum == RDReaderView.blankPageNum { return coverIndex } if pageNum == coverIndex + 1 { return RDReaderView.blankPageNum } } let prev = pageNum - 1 return prev >= 0 ? prev : nil } /// UIPageViewController 数据源:返回当前页之前(左/上)的页面控制器 /// RTL 模式下 before/after 语义互换:before(向右翻)= 下一页 public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let vc = viewController as? RDReaderPageChildViewController else { return nil } let isDualPage = landscapeDualPageEnabled && isLandscape let isRTL = pageDirection == .rightToLeft let targetNum = isRTL ? nextPageNum(after: vc.pageNum, isDualPage: isDualPage) : prevPageNum(before: vc.pageNum, isDualPage: isDualPage) guard let num = targetNum else { return nil } let targetVC = makeSinglePageChildVC(for: num) willPreviousTransitionToViewController = targetVC return targetVC } /// UIPageViewController 数据源:返回当前页之后(右/下)的页面控制器 /// RTL 模式下 before/after 语义互换 public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let vc = viewController as? RDReaderPageChildViewController else { return nil } let isDualPage = landscapeDualPageEnabled && isLandscape let isRTL = pageDirection == .rightToLeft let targetNum = isRTL ? prevPageNum(before: vc.pageNum, isDualPage: isDualPage) : nextPageNum(after: vc.pageNum, isDualPage: isDualPage) guard let num = targetNum else { return nil } let targetVC = makeSinglePageChildVC(for: num) willNextTransitionToViewController = targetVC return targetVC } /// UIPageViewController 代理:翻页动画完成回调 /// 更新当前页码,触发预加载,并检测 PageViewController 异常状态 public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed, let firstVC = pageViewController.viewControllers?.first as? RDReaderPageChildViewController { let pn = firstVC.pageNum if pn != RDReaderView.blankPageNum && pn != RDReaderView.blankEndPageNum { currentPage = pn primePageCache(around: pn, preferredForward: predictedPageDirection) } } predictedPageDirection = nil finishPageCurlTransition() } /// UIPageViewController 代理:即将开始转场动画 /// 记录预测的翻页方向,用于优化预加载策略 public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { pagingController.isTransitioning = true willTransitionToViewController = pendingViewControllers.first if let target = pendingViewControllers.first as? RDReaderPageChildViewController, target.pageNum != RDReaderView.blankPageNum, target.pageNum != RDReaderView.blankEndPageNum { predictedPageDirection = target.pageNum >= currentPage primePageCache(around: target.pageNum, preferredForward: predictedPageDirection) } } }