feat(epub): complete wxread parity scope

This commit is contained in:
shen
2026-05-26 09:19:37 +08:00
parent 23182e8b5a
commit 0e7d952fe5
31 changed files with 2619 additions and 1533 deletions
@@ -287,6 +287,13 @@ public class RDReaderView: UIView {
}
}
///
private func clampedPageNumber(_ pageNum: Int) -> Int? {
let totalPages = dataSource?.pageCountOfReaderView(readerView: self) ?? 0
guard totalPages > 0 else { return nil }
return min(max(pageNum, 0), totalPages - 1)
}
/// layoutSubviews
private var previousIsLandscape: Bool?
@@ -339,6 +346,14 @@ public class RDReaderView: UIView {
let boundsSize: CGSize
}
/// pageCurl
private struct PageTransitionRequest: Equatable {
let pageNum: Int
let animated: Bool
}
private var pendingTransitionRequest: PageTransitionRequest?
override init(frame: CGRect) {
super.init(frame: frame)
}
@@ -465,12 +480,41 @@ public class RDReaderView: UIView {
guard currentPage >= 0 else { return }
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.isTransitioning = false
self.pendingTransitionRequest = nil
self.willPreviousTransitionToViewController = nil
self.willNextTransitionToViewController = nil
self.willTransitionToViewController = nil
self.invalidatePageCaches()
self.rebuildPageViewController()
self.transitionToPage(pageNum: self.currentPage, animated: false)
}
}
/// pageCurl
private func shouldQueuePageTransition(_ request: PageTransitionRequest) -> Bool {
guard currentDisplayType == .pageCurl, isTransitioning else { return false }
pendingTransitionRequest = request
return true
}
/// pageCurl
private func finishPageCurlTransition(repairFaultIfNeeded: Bool = true) {
isTransitioning = false
willPreviousTransitionToViewController = nil
willNextTransitionToViewController = nil
willTransitionToViewController = nil
if repairFaultIfNeeded, detectPageViewControllerFault(pageViewController) {
patchPageViewControllerFault()
return
}
guard let pendingTransitionRequest else { return }
self.pendingTransitionRequest = nil
transitionToPage(pageNum: pendingTransitionRequest.pageNum, animated: pendingTransitionRequest.animated)
}
// MARK: - Preload / Forecast
/// 宿
@@ -706,6 +750,10 @@ public class RDReaderView: UIView {
if didAddToParent {
pageViewController.didMove(toParent: parentViewController)
}
if currentDisplayType == .pageCurl, !isTransitioning, pendingTransitionRequest != nil {
finishPageCurlTransition(repairFaultIfNeeded: false)
}
}
/// pageViewController
@@ -900,45 +948,65 @@ public class RDReaderView: UIView {
/// - pageNum: item
/// - animated:
public func transitionToPage(pageNum: Int, animated: Bool = false) {
guard let safePageNum = clampedPageNumber(pageNum) else { return }
switch currentDisplayType {
case .pageCurl:
let request = PageTransitionRequest(pageNum: safePageNum, animated: animated)
if shouldQueuePageTransition(request) {
return
}
let isDualPage = landscapeDualPageEnabled && isLandscape
// RTL
let direction: UIPageViewController.NavigationDirection
if pageDirection == .rightToLeft {
direction = pageNum > currentPage ? .reverse : .forward
direction = safePageNum > currentPage ? .reverse : .forward
} else {
direction = pageNum > currentPage ? .forward : .reverse
direction = safePageNum > currentPage ? .forward : .reverse
}
predictedPageDirection = currentPage >= 0 ? safePageNum >= currentPage : nil
attachPageViewControllerIfNeeded()
if isDualPage {
let pair = dualPagePair(for: pageNum)
let pair = dualPagePair(for: safePageNum)
let leftContent = pageViewForDisplay(pageNum: pair.left)
let leftVC = RDReaderPageChildViewController(contentView: leftContent, pageNum: pair.left)
isTransitioning = animated
if let rightPage = pair.right {
let rightContent = pageViewForDisplay(pageNum: rightPage)
let rightVC = RDReaderPageChildViewController(contentView: rightContent, pageNum: rightPage)
pageViewController.setViewControllers([leftVC, rightVC], direction: animated ? direction : .forward, animated: animated, completion: nil)
pageViewController.setViewControllers([leftVC, rightVC], direction: animated ? direction : .forward, animated: animated) { [weak self] _ in
guard let self else { return }
self.finishPageCurlTransition()
}
} else {
//
let blankNum = isFullScreenPage(pair.left) ? RDReaderView.blankPageNum : RDReaderView.blankEndPageNum
let emptyVC = RDReaderPageChildViewController(contentView: UIView(), pageNum: blankNum)
pageViewController.setViewControllers([leftVC, emptyVC], direction: animated ? direction : .forward, animated: animated, completion: nil)
pageViewController.setViewControllers([leftVC, emptyVC], direction: animated ? direction : .forward, animated: animated) { [weak self] _ in
guard let self else { return }
self.finishPageCurlTransition()
}
}
currentPage = pair.left
primePageCache(around: pair.left, preferredForward: predictedPageDirection)
} else {
let contentView = pageViewForDisplay(pageNum: pageNum)
let vc = RDReaderPageChildViewController(contentView: contentView, pageNum: pageNum)
pageViewController.setViewControllers([vc], direction: animated ? direction : .forward, animated: animated, completion: nil)
currentPage = pageNum
primePageCache(around: pageNum, preferredForward: predictedPageDirection)
let contentView = pageViewForDisplay(pageNum: safePageNum)
let vc = RDReaderPageChildViewController(contentView: contentView, pageNum: safePageNum)
isTransitioning = animated
pageViewController.setViewControllers([vc], direction: animated ? direction : .forward, animated: animated) { [weak self] _ in
guard let self else { return }
self.finishPageCurlTransition()
}
currentPage = safePageNum
primePageCache(around: safePageNum, preferredForward: predictedPageDirection)
}
default:
collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.setContentOffset(layout.currentContentOffset(count: pageNum), animated: animated)
currentPage = pageNum
primePageCache(around: pageNum, preferredForward: predictedPageDirection)
predictedPageDirection = currentPage >= 0 ? safePageNum >= currentPage : nil
collectionView.setContentOffset(layout.currentContentOffset(count: safePageNum), animated: animated)
currentPage = safePageNum
primePageCache(around: safePageNum, preferredForward: predictedPageDirection)
}
}
@@ -1091,15 +1159,13 @@ extension RDReaderView: UIPageViewControllerDataSource, UIPageViewControllerDele
}
predictedPageDirection = nil
if detectPageViewControllerFault(pageViewController) {
patchPageViewControllerFault()
}
finishPageCurlTransition()
}
/// UIPageViewController
///
public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
isTransitioning = true
willTransitionToViewController = pendingViewControllers.first
if let target = pendingViewControllers.first as? RDReaderPageChildViewController,
target.pageNum != RDReaderView.blankPageNum,
@@ -1151,6 +1217,9 @@ extension RDReaderView: UICollectionViewDataSource, RDReaderFlowLayoutDelegate,
///
public func pageNum(flowLayout: RDReaderFlowLayout, pageIndex: Int) {
if currentPage >= 0, currentPage != pageIndex {
predictedPageDirection = pageIndex >= currentPage
}
currentPage = pageIndex
primePageCache(around: pageIndex, preferredForward: predictedPageDirection)
}