fix(reader): stabilize pagination display and page curl reuse

This commit is contained in:
shen
2026-05-26 21:37:39 +08:00
parent 83b705b9ae
commit 736902b489
4 changed files with 104 additions and 23 deletions
@@ -13,12 +13,18 @@ final class RDEPUBPageInteractionController {
var snapshot: RDEPUBPageLayoutSnapshot?
private var dtLayoutFrame: DTCoreTextLayoutFrame?
private var contentOffsetBase: Int = 0
#if canImport(DTCoreText)
func configure(layoutFrame: DTCoreTextLayoutFrame?, page: RDEPUBTextPage?) {
func configure(layoutFrame: DTCoreTextLayoutFrame?, page: RDEPUBTextPage?, contentOffsetBase: Int = 0) {
dtLayoutFrame = layoutFrame
self.contentOffsetBase = contentOffsetBase
if let layoutFrame, let page {
snapshot = RDEPUBPageLayoutSnapshot.build(from: layoutFrame, page: page)
snapshot = RDEPUBPageLayoutSnapshot.build(
from: layoutFrame,
page: page,
contentOffsetBase: contentOffsetBase
)
} else {
snapshot = nil
}
@@ -47,7 +53,11 @@ final class RDEPUBPageInteractionController {
)
let idx = dtLine.stringIndex(forPosition: relativePoint)
guard idx != NSNotFound, idx >= 0 else { return nil }
return normalizedIndex(idx, lineRange: line.stringRange, pageRange: snapshot.pageContentRange)
return normalizedIndex(
absoluteIndex(for: idx),
lineRange: line.stringRange,
pageRange: snapshot.pageContentRange
)
#else
return nil
#endif
@@ -75,9 +85,9 @@ final class RDEPUBPageInteractionController {
#if canImport(DTCoreText)
guard let dtLine = dtLineContaining(range: line.stringRange) else { continue }
let startX = dtLine.offset(forStringIndex: overlap.location)
let startX = dtLine.offset(forStringIndex: localIndex(for: overlap.location))
let endIdx = overlap.location + overlap.length
let endX = dtLine.offset(forStringIndex: endIdx)
let endX = dtLine.offset(forStringIndex: localIndex(for: endIdx))
#else
let startX: CGFloat = 0
let endX: CGFloat = line.frame.width
@@ -135,7 +145,7 @@ final class RDEPUBPageInteractionController {
#if canImport(DTCoreText)
guard let dtLine = dtLineContaining(range: line.stringRange) else { return nil }
let offsetX = dtLine.offset(forStringIndex: index)
let offsetX = dtLine.offset(forStringIndex: localIndex(for: index))
#else
let offsetX: CGFloat = 0
#endif
@@ -187,10 +197,18 @@ final class RDEPUBPageInteractionController {
#if canImport(DTCoreText)
private func dtLineContaining(range: NSRange) -> DTCoreTextLayoutLine? {
guard let dtLayoutFrame else { return nil }
return dtLayoutFrame.lineContaining(UInt(range.location))
return dtLayoutFrame.lineContaining(UInt(max(localIndex(for: range.location), 0)))
}
#endif
private func localIndex(for absoluteIndex: Int) -> Int {
absoluteIndex - contentOffsetBase
}
private func absoluteIndex(for localIndex: Int) -> Int {
localIndex + contentOffsetBase
}
private func mergeAdjacentRects(_ rects: [CGRect]) -> [CGRect] {
guard rects.count > 1 else { return rects }