feat: complete large-book pagination runtime and UI coverage
This commit is contained in:
@@ -51,6 +51,8 @@ public final class RDURLReaderController: UIViewController {
|
||||
private var isRetryingPendingDemoPage = false
|
||||
private let maxPendingDemoPageAttempts = 24
|
||||
private let pendingDemoPageRetryDelay: TimeInterval = 0.25
|
||||
private var demoStateTimer: Timer?
|
||||
private var lastEmittedDemoState = ""
|
||||
|
||||
/// 初始化方法
|
||||
/// - Parameters:
|
||||
@@ -81,7 +83,17 @@ public final class RDURLReaderController: UIViewController {
|
||||
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
emitDemoState()
|
||||
startDemoStateTimerIfNeeded()
|
||||
refreshDemoState()
|
||||
}
|
||||
|
||||
public override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
stopDemoStateTimer()
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopDemoStateTimer()
|
||||
}
|
||||
|
||||
/// 切换阅读器的翻页模式(Demo 用)
|
||||
@@ -155,8 +167,8 @@ public final class RDURLReaderController: UIViewController {
|
||||
edgeInsets: epubConfiguration.reflowableContentInsets,
|
||||
numberOfColumns: 1,
|
||||
columnGap: 20,
|
||||
avoidOrphans: true,
|
||||
avoidWidows: true,
|
||||
avoidOrphans: false,
|
||||
avoidWidows: false,
|
||||
avoidPageBreakInsideEnabled: true,
|
||||
hyphenation: true,
|
||||
imageMaxHeightRatio: 0.85
|
||||
@@ -205,15 +217,17 @@ public final class RDURLReaderController: UIViewController {
|
||||
guard let readerController else { return false }
|
||||
guard pageNumber > 0 else { return false }
|
||||
|
||||
let totalPages = readerController.textBook?.pages.count ?? readerController.activePages.count
|
||||
let knownPages = readerController.readerContext.bookPageMap?.totalPages
|
||||
?? readerController.textBook?.pages.count
|
||||
?? readerController.activePages.count
|
||||
let numPages = readerController.readerView.numberOfPages()
|
||||
print("[ReadViewDemo] performDemoPageNavigation(\(pageNumber)): textBook.pages=\(totalPages), readerView.numberOfPages=\(numPages), currentPage=\(readerController.readerView.currentPage)")
|
||||
guard pageNumber <= totalPages else { return false }
|
||||
|
||||
readerController.readerView.transitionToPage(pageNum: pageNumber - 1, animated: animated)
|
||||
print("[ReadViewDemo] performDemoPageNavigation(\(pageNumber)): after transition, currentPage=\(readerController.readerView.currentPage)")
|
||||
emitDemoState(prefix: "page=\(pageNumber)")
|
||||
return true
|
||||
print("[ReadViewDemo] performDemoPageNavigation(\(pageNumber)): knownPages=\(knownPages), readerView.numberOfPages=\(numPages), currentPage=\(readerController.readerView.currentPage)")
|
||||
let moved = readerController.go(toPageNumber: pageNumber, animated: animated)
|
||||
print("[ReadViewDemo] performDemoPageNavigation(\(pageNumber)): moved=\(moved), after currentPage=\(readerController.readerView.currentPage)")
|
||||
if moved {
|
||||
emitDemoState(prefix: "page=\(pageNumber)")
|
||||
}
|
||||
return moved
|
||||
}
|
||||
|
||||
private func queuePendingDemoPageNavigation(_ pageNumber: Int, animated: Bool) {
|
||||
@@ -288,19 +302,87 @@ public final class RDURLReaderController: UIViewController {
|
||||
])
|
||||
}
|
||||
|
||||
private func startDemoStateTimerIfNeeded() {
|
||||
guard demoStateTimer == nil else { return }
|
||||
demoStateTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] _ in
|
||||
self?.refreshDemoState(logIfChanged: false)
|
||||
}
|
||||
if let demoStateTimer {
|
||||
RunLoop.main.add(demoStateTimer, forMode: .common)
|
||||
}
|
||||
}
|
||||
|
||||
private func stopDemoStateTimer() {
|
||||
demoStateTimer?.invalidate()
|
||||
demoStateTimer = nil
|
||||
}
|
||||
|
||||
private func emitDemoState(prefix: String? = nil) {
|
||||
refreshDemoState(logPrefix: prefix, logIfChanged: true)
|
||||
}
|
||||
|
||||
private func refreshDemoState(logPrefix: String? = nil, logIfChanged: Bool = true) {
|
||||
let page = readerController?.currentPageNumber.map(String.init) ?? "nil"
|
||||
let display = readerController?.configuration.displayType.demoArgumentValue ?? epubConfiguration.displayType.demoArgumentValue
|
||||
let toolbar = readerController?.readerView.isShowToolView == true ? "visible" : "hidden"
|
||||
let highlights = readerController?.highlights.count ?? 0
|
||||
let selection = readerController?.currentSelection == nil ? 0 : 1
|
||||
let state = "reader=opened page=\(page) display=\(display) toolbar=\(toolbar) highlights=\(highlights) selection=\(selection)"
|
||||
let location = readerController?.currentLocation
|
||||
let href = encodedDemoLocationHref(location?.href)
|
||||
let progression = location.map { String(format: "%.4f", $0.navigationProgression) } ?? "nil"
|
||||
let mapSnapshot = demoPaginationSnapshot()
|
||||
let layoutConfig = readerController?.readerContext.currentTextLayoutConfig(pageSize: currentTextPageSize())
|
||||
let state = [
|
||||
"reader=opened",
|
||||
"page=\(page)",
|
||||
"display=\(display)",
|
||||
"toolbar=\(toolbar)",
|
||||
"highlights=\(highlights)",
|
||||
"selection=\(selection)",
|
||||
"href=\(href)",
|
||||
"progression=\(progression)",
|
||||
"mode=\(mapSnapshot.mode)",
|
||||
"pagination=\(mapSnapshot.phase)",
|
||||
"knownPages=\(mapSnapshot.knownPages)",
|
||||
"knownChapters=\(mapSnapshot.knownChapters)",
|
||||
"buildableChapters=\(mapSnapshot.buildableChapters)",
|
||||
"avoidWidows=\(layoutConfig?.avoidWidows == true ? 1 : 0)",
|
||||
"avoidOrphans=\(layoutConfig?.avoidOrphans == true ? 1 : 0)"
|
||||
].joined(separator: " ")
|
||||
demoStateLabel.text = state
|
||||
if let prefix {
|
||||
logDemoState(prefix: prefix)
|
||||
} else {
|
||||
if let logPrefix {
|
||||
logDemoState(prefix: logPrefix)
|
||||
} else if logIfChanged, state != lastEmittedDemoState {
|
||||
print("[ReadViewDemo] automation \(state)")
|
||||
}
|
||||
lastEmittedDemoState = state
|
||||
}
|
||||
|
||||
private func encodedDemoLocationHref(_ href: String?) -> String {
|
||||
guard let href, !href.isEmpty else { return "nil" }
|
||||
return href.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? href.replacingOccurrences(of: " ", with: "%20")
|
||||
}
|
||||
|
||||
private func demoPaginationSnapshot() -> (mode: String, phase: String, knownPages: Int, knownChapters: Int, buildableChapters: Int) {
|
||||
guard let readerController else {
|
||||
return ("unavailable", "none", 0, 0, 0)
|
||||
}
|
||||
|
||||
if let bookPageMap = readerController.readerContext.bookPageMap {
|
||||
let buildableChapters = readerController.publication?.spine.filter {
|
||||
$0.linear && ($0.mediaType.contains("html") || $0.mediaType.contains("xhtml"))
|
||||
}.count ?? 0
|
||||
let phase = buildableChapters > 0 && bookPageMap.totalChapters >= buildableChapters ? "full" : "partial"
|
||||
return ("bookPageMap", phase, bookPageMap.totalPages, bookPageMap.totalChapters, buildableChapters)
|
||||
}
|
||||
|
||||
if let textBook = readerController.textBook {
|
||||
return ("textBook", "full", textBook.pages.count, textBook.chapters.count, textBook.chapters.count)
|
||||
}
|
||||
|
||||
let snapshotChapters = readerController.activeChapters.count
|
||||
let snapshotPages = readerController.activePages.count
|
||||
return ("snapshot", snapshotPages > 0 ? "full" : "none", snapshotPages, snapshotChapters, snapshotChapters)
|
||||
}
|
||||
|
||||
/// 计算文本分页的页面尺寸。
|
||||
|
||||
Reference in New Issue
Block a user