fix: pageCurl翻页后偶尔刷新回章节第一页

根因:.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 <noreply@anthropic.com>
This commit is contained in:
shenlei 2026-06-25 16:52:38 +08:00
parent d7f28c3f10
commit c555329948
2 changed files with 23 additions and 4 deletions

View File

@ -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
)

View File

@ -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 {
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 {