From 5ae7823ef8c6e92bcc04c8d5a165aa84d26e09ab Mon Sep 17 00:00:00 2001 From: shenlei Date: Mon, 6 Jul 2026 11:56:22 +0900 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E9=87=8F=E6=8D=A2=E8=A1=A8=E5=89=8D?= =?UTF-8?q?=E7=94=A8=E6=97=A7=E9=A1=B5=E8=A1=A8=E5=9B=9E=E7=8E=AF=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E5=BD=93=E5=89=8D=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 初始 partial 窗口被全书页表接管时,live 页码(窗口坐标)与新表解析 页码(全书坐标)不可比,±1 启发式误判后 preserveLivePage 会把模型 位置钉在全书第 1 页,导致位置错乱并级联阻塞后续全表提交。改为换表 前先用旧表解析 currentLocation:回环等于 live 页说明位置忠实反映 屏幕内容,换表后无条件信任新表解析结果;不等时才退回 ±1 门槛,保留 对 stale 持久化位置的防护。 Co-Authored-By: Claude Fable 5 --- .../RDEPUBPresentationRuntime.swift | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift index 9d34f6e..e35593d 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift @@ -160,6 +160,11 @@ final class RDEPUBPresentationRuntime { ) { let currentLocation = locationCoordinator.currentVisibleLocation() let livePageBeforeApply = readerView.currentPage + 1 + // Resolved against the outgoing page map. When it round-trips to the live + // page, the location faithfully describes what is on screen, so whatever + // page it resolves to in the new map is authoritative even if the two maps + // number pages differently (partial-window -> full-book takeover). + let oldResolvedPage = currentLocation.flatMap { controller.pageNumber(for: $0) } context.textBook = nil applyPageMapToLiveModel(newPageMap) @@ -172,11 +177,12 @@ final class RDEPUBPresentationRuntime { let resolvedTargetPage = controller.pageNumber(for: currentLocation) let shouldTrustResolvedLocation = shouldTrustFullReplaceResolvedPage( resolvedTargetPage, - livePageBeforeApply: livePageBeforeApply + livePageBeforeApply: livePageBeforeApply, + locationMatchesLivePage: oldResolvedPage == livePageBeforeApply ) RDEPUBBackgroundTrace.log( "Reconciliation", - "applyFullPageMapReplacement decision livePage=\(livePageBeforeApply) resolvedTargetPage=\(resolvedTargetPage ?? -1) trustResolved=\(shouldTrustResolvedLocation) href=\(currentLocation.href)" + "applyFullPageMapReplacement decision livePage=\(livePageBeforeApply) oldResolvedPage=\(oldResolvedPage ?? -1) resolvedTargetPage=\(resolvedTargetPage ?? -1) trustResolved=\(shouldTrustResolvedLocation) href=\(currentLocation.href)" ) if shouldTrustResolvedLocation, @@ -433,9 +439,16 @@ final class RDEPUBPresentationRuntime { private func shouldTrustFullReplaceResolvedPage( _ resolvedTargetPage: Int?, - livePageBeforeApply: Int + livePageBeforeApply: Int, + locationMatchesLivePage: Bool ) -> Bool { guard let resolvedTargetPage else { return false } + if locationMatchesLivePage { + return true + } + // The location did not round-trip to the live page in the outgoing map + // (stale persisted location or mid-transition), so only follow it when it + // stays next to the page the user is actually looking at. return abs(resolvedTargetPage - livePageBeforeApply) <= 1 }