ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift
shenlei d15f20b097 feat: 交互协调器拆分、附件提示、暗色图片适配、选区放大镜及文档清理
- 拆分 ContentDelegates/TextContentView 为独立协调器(InteractionCoordinator、LocationResolution、ExternalLinks、AttachmentTooltip)
- 新增 RDEPUBAttachmentTooltipView/OverlayView 附件气泡提示
- 新增 RDEPUBDarkImageAdjuster 暗色模式图片亮度适配
- 新增 RDEPUBSelectionLoupeView 选区放大镜
- 新增 MetadataParseWorker/CancellationController 元数据解析取消机制
- 重构 PresentationRuntime/PaginationCoordinator 精简职责
- 优化 ChapterLoader/WarmupOrchestrator 异步章节加载
- CFI 模块微调与 NoteModels 更新
- 清理冗余文档,更新架构/UML/业务逻辑文档

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-24 17:47:24 +08:00

123 lines
4.4 KiB
Swift

import UIKit
import WebKit
extension RDEPUBWebView {
public func loadFixedSpread(
parser: RDEPUBParser,
spread: EPUBFixedSpread,
viewportSize: CGSize,
contentInset: UIEdgeInsets,
backgroundColor: UIColor?
) {
let request = RDEPUBRenderRequest.fixed(
RDEPUBFixedRenderRequest(
spread: spread,
viewportSize: viewportSize,
contentInset: contentInset,
backgroundColorCSS: backgroundColor?.rd_hexString,
fit: .page,
searchPresentation: nil
)
)
load(publication: parser.makePublication(), request: request)
}
func handleFixedLayoutLoad(
publication: RDEPUBPublication,
request: RDEPUBFixedRenderRequest,
loadSignature: String,
in webView: WKWebView
) {
currentPageIndex = 0
currentTotalPagesInChapter = 1
viewportSize = request.viewportSize
currentPadding = request.contentInset
currentFontSize = 16
currentLineHeightMultiple = 1.5
currentThemeBackgroundColor = request.backgroundColorCSS
currentThemeTextColor = nil
targetLocation = nil
pendingHighlights = []
fixedSpread = request.spread
isFixedLayout = true
pendingProgressionRequest = false
currentLoadSignature = loadSignature
scheduleFixedLayoutReadyFallback()
let html = RDEPUBFixedLayoutTemplate.html(for: request, publication: publication)
RDEPUBWebViewDebug.log(debugScope, message: "load fixed spread resources=\(request.spread.resources.map(\.href).joined(separator: ",")) fit=\(request.fit.rawValue)")
webView.loadHTMLString(
html,
baseURL: URL(string: "\(RDEPUBResourceURLSchemeHandler.scheme)://\(RDEPUBResourceURLSchemeHandler.host)/")
)
}
func fixedLayoutLoadSignature(publicationKey: String, request: RDEPUBFixedRenderRequest) -> String {
let searchSignature = [
request.searchPresentation?.keyword ?? "",
request.searchPresentation?.resources.map {
[
$0.href,
String($0.matchCount),
String($0.activeLocalMatchIndex ?? -1)
].joined(separator: "|")
}.joined(separator: ",") ?? ""
].joined(separator: "#")
return [
publicationKey,
"fixed",
request.spread.resources.map(\.href).joined(separator: "|"),
String(format: "%.2f", request.viewportSize.width),
String(format: "%.2f", request.viewportSize.height),
String(format: "%.2f", request.contentInset.top),
String(format: "%.2f", request.contentInset.left),
String(format: "%.2f", request.contentInset.bottom),
String(format: "%.2f", request.contentInset.right),
request.backgroundColorCSS ?? "",
request.fit.rawValue,
searchSignature
].joined(separator: "#")
}
func scheduleFixedLayoutReadyFallback() {
let workItem = DispatchWorkItem { [weak self] in
RDEPUBWebViewDebug.log(self?.debugScope ?? "ReaderWebView", message: "fixed ready fallback fired")
self?.applyFixedPresentationIfNeeded {
self?.refreshNativeDecorationsIfNeeded {
self?.rendered()
}
}
}
fixedLayoutReadyWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: workItem)
}
func cancelFixedLayoutReadyFallback() {
fixedLayoutReadyWorkItem?.cancel()
fixedLayoutReadyWorkItem = nil
}
func applyFixedPresentationIfNeeded(completion: (() -> Void)? = nil) {
guard let webView,
let currentRenderRequest,
case .fixed(let request) = currentRenderRequest else {
completion?()
return
}
let script = RDEPUBJavaScriptBridge.applyFixedPresentationScript(for: request)
webView.evaluateJavaScript(script) { [weak self] _, error in
guard let self else {
completion?()
return
}
if let error {
self.delegate?.epubWebView(self, didLogJavaScriptError: error.localizedDescription)
}
self.applySearchDecorationsIfNeeded(completion: completion)
}
}
}