From c555329948e03ae15863438d6866aa2af0946fa2 Mon Sep 17 00:00:00 2001 From: shenlei Date: Thu, 25 Jun 2026 16:52:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20pageCurl=E7=BF=BB=E9=A1=B5=E5=90=8E?= =?UTF-8?q?=E5=81=B6=E5=B0=94=E5=88=B7=E6=96=B0=E5=9B=9E=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:.extendPartial pageMap更新提交时使用了异步扩展开始时捕获的 currentPageNumber/currentLocation,在pageCurl模式下用户可能已经翻了几页, 导致过时值通过transitionToPage把用户拉回旧位置。 修复三点: 1. PresentationRuntime: .extendPartial fallback不再使用捕获的 currentPageNumber,改用readerView.currentPage实时值 2. ChapterWarmupOrchestrator: 异步加载完成回调中使用实时位置 (readerView.currentPage + locationCoordinator.currentVisibleLocation()) 而非异步开始时捕获的旧值 3. PresentationRuntime: rebindVisiblePage在pageCurl模式下, 如果正在翻页动画中(isPageCurlTransitioning),延迟到动画结束后 再用实时currentPage执行transitionToPage,避免在pageNum回调栈 内同步触发transitionToPage导致重入 Co-Authored-By: Claude --- .../RDEPUBChapterWarmupOrchestrator.swift | 8 ++++++-- .../RDEPUBPresentationRuntime.swift | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift index 5e0fd55..072c45d 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift @@ -183,9 +183,13 @@ final class RDEPUBChapterWarmupOrchestrator { group.notify(queue: .main) { [weak self] in guard let self else { return } defer { self.endPartialBookPageMapExtension() } + // Use live position values rather than the stale captured values, + // since the user may have turned several pages since the extension began. + let livePageNumber = max(self.context.readerView?.currentPage ?? 0, 0) + 1 + let liveLocation = self.locationCoordinator.currentVisibleLocation() self.applyAsyncPartialBookPageMapExtension( - currentPageNumber: currentPageNumber, - currentLocation: currentLocation, + currentPageNumber: livePageNumber, + currentLocation: liveLocation, currentMap: currentMap, loadedChapters: loadedChapters ) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift index d46c49c..433116c 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift @@ -248,8 +248,13 @@ final class RDEPUBPresentationRuntime { rebindVisibleLocation(currentLocation, readerView: readerView, controller: controller) { return true } + // Fallback: prefer the live readerView.currentPage over the stale captured + // currentPageNumber, which may be outdated by the time this commit runs + // (especially in pageCurl mode where the user may have turned several pages + // since the extension was initiated). + let livePageIndex = max(readerView.currentPage, 0) rebindVisiblePage( - to: max(currentPageNumber - 1, 0), + to: livePageIndex, readerView: readerView ) return true @@ -264,7 +269,17 @@ final class RDEPUBPresentationRuntime { private func rebindVisiblePage(to pageIndex: Int, readerView: RDReaderView) { if readerView.currentDisplayType == .pageCurl { - readerView.transitionToPage(pageNum: pageIndex, animated: false) + if readerView.isPageCurlTransitioning { + // Defer the transition until the current page-curl animation completes, + // and re-read the live page at that time to avoid jumping to a stale position. + DispatchQueue.main.async { [weak readerView] in + guard let readerView, !readerView.isPageCurlTransitioning else { return } + let livePageIndex = max(readerView.currentPage, 0) + readerView.transitionToPage(pageNum: livePageIndex, animated: false) + } + } else { + readerView.transitionToPage(pageNum: pageIndex, animated: false) + } } else { readerView.reloadPageCountOnly() if pageIndex != readerView.currentPage {