import UIKit extension RDPDFReaderView: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { pageCount() } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let view = dataSource?.pageContentView(readerView: self, pageNum: indexPath.item) ?? UIView() view.tag = indexPath.item configureSpreadAlignment(for: view, page: indexPath.item) configureInteraction(for: view) if let cell = view.superview?.superview as? RDPDFReaderContentCell { return cell } let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "fallback", for: indexPath) as! RDPDFReaderContentCell cell.hostedView = view return cell } public func collectionView( _ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath ) -> CGSize { // 手机横屏竖滑:cell 高度=宽度适配后的纸张高度,超出一屏的内容由 // collectionView 自身的连续滚动承接;页面未加载时先按一屏高占位。 if usesWidthFitVerticalScroll, let height = verticalScrollWidthFitPageHeightProvider?(indexPath.item, bounds.width), height > 0 { return CGSize(width: bounds.width, height: height) } return usesLandscapeSpread ? CGSize(width: bounds.width / 2, height: bounds.height) : bounds.size } public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { updateCurrentPageAfterScroll(scrollView) } public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { // 竖滑是连续滚动(isPagingEnabled = false),轻推后直接停下不会触发 // 减速回调;此处兜底,否则页码停留在旧值。 if !decelerate { updateCurrentPageAfterScroll(scrollView) } } private func updateCurrentPageAfterScroll(_ scrollView: UIScrollView) { guard currentDisplayType != .pageCurl else { return } if currentDisplayType == .verticalScroll { // 宽度适配下各 cell 高度不同,页码取视口中心命中的 cell。 // 顶部/底部回弹时中心点会越出内容范围,钳制回内容内再取 cell。 let centerY = scrollView.contentOffset.y + scrollView.bounds.height / 2 let center = CGPoint( x: scrollView.bounds.width / 2, y: min(max(0, centerY), max(0, collectionView.contentSize.height - 1)) ) if let indexPath = collectionView.indexPathForItem(at: center) { currentPage = indexPath.item } else if let first = collectionView.indexPathsForVisibleItems.min(by: { $0.item < $1.item }) { currentPage = first.item } return } let value = scrollView.contentOffset.x / max(bounds.width, 1) currentPage = Int(value.rounded()) * (usesLandscapeSpread ? 2 : 1) } }