- Refactor chapter runtime: replace window coordinator/snapshot with warmup orchestrator - Update EPUB core: parser, reading session, JS bridge, navigator layout - Update reader controller: data source, location resolution, persistence - Update chapter runtime: data cache, loader, runtime store, disk cache, warmup orchestrator - Remove deprecated navigation state machine and pagination state - Update text rendering: book cache, HTML normalizer - Update UI: text content view, dark image adjuster, text selection controller - Update settings and reader configuration - Add CODE_REVIEW.md and AUDIT_FINAL.md documentation - Update pod dependencies (remove SSAlertSwift, SnapKit) - Update podspec and pod configuration files Co-Authored-By: Claude <noreply@anthropic.com>
110 lines
4.6 KiB
Swift
110 lines
4.6 KiB
Swift
|
|
import UIKit
|
|
import WebKit
|
|
|
|
extension RDEPUBWebView {
|
|
|
|
func configureWebViewIfNeeded(publication: RDEPUBPublication) {
|
|
let parser = publication.parser
|
|
let publicationKey = parser.opfURL?.path ?? parser.extractionRootURL?.path ?? UUID().uuidString
|
|
if publicationKey == configuredPublicationKey, webView != nil {
|
|
return
|
|
}
|
|
|
|
teardownWebView()
|
|
|
|
let userContentController = WKUserContentController()
|
|
RDEPUBJavaScriptBridge.messageNames.forEach {
|
|
userContentController.add(self, name: $0)
|
|
}
|
|
userContentController.addUserScript(
|
|
WKUserScript(
|
|
source: RDEPUBJavaScriptBridge.userScript,
|
|
injectionTime: .atDocumentEnd,
|
|
forMainFrameOnly: true
|
|
)
|
|
)
|
|
|
|
let configuration = WKWebViewConfiguration()
|
|
configuration.websiteDataStore = .nonPersistent()
|
|
configuration.userContentController = userContentController
|
|
let schemeHandler = RDEPUBResourceURLSchemeHandler(parser: parser)
|
|
configuration.setURLSchemeHandler(schemeHandler, forURLScheme: RDEPUBResourceURLSchemeHandler.scheme)
|
|
|
|
let webView = RDEPUBAnnotationWebView(frame: bounds, configuration: configuration)
|
|
webView.onSelectionAction = { [weak self] action in
|
|
guard let self else { return }
|
|
self.handleSelectionMenuAction(action)
|
|
}
|
|
// M-15: Use UIEditMenuInteraction on iOS 16+ to replace deprecated UIMenuController
|
|
if #available(iOS 16.0, *) {
|
|
let interaction = UIEditMenuInteraction(delegate: webView)
|
|
webView.addInteraction(interaction)
|
|
webView._editMenuInteraction = interaction
|
|
} else {
|
|
UIMenuController.shared.menuItems = [
|
|
UIMenuItem(title: "拷贝", action: #selector(RDEPUBAnnotationWebView.rd_copy(_:))),
|
|
UIMenuItem(title: "高亮", action: #selector(RDEPUBAnnotationWebView.rd_highlight(_:))),
|
|
UIMenuItem(title: "批注", action: #selector(RDEPUBAnnotationWebView.rd_annotate(_:)))
|
|
]
|
|
}
|
|
webView.navigationDelegate = self
|
|
webView.scrollView.isScrollEnabled = false
|
|
webView.scrollView.showsHorizontalScrollIndicator = false
|
|
webView.scrollView.showsVerticalScrollIndicator = false
|
|
webView.scrollView.bounces = false
|
|
webView.scrollView.alwaysBounceHorizontal = false
|
|
webView.scrollView.alwaysBounceVertical = false
|
|
webView.scrollView.clipsToBounds = true
|
|
webView.scrollView.panGestureRecognizer.isEnabled = false
|
|
webView.scrollView.pinchGestureRecognizer?.isEnabled = false
|
|
webView.isOpaque = false
|
|
webView.backgroundColor = .clear
|
|
webView.clipsToBounds = true
|
|
if #available(iOS 16.4, *) {
|
|
webView.isInspectable = RDEPUBWebViewDebug.isInspectableEnabled
|
|
}
|
|
|
|
addSubview(webView)
|
|
webView.frame = bounds
|
|
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
|
|
self.schemeHandler = schemeHandler
|
|
self.webView = webView
|
|
self.configuredPublicationKey = publicationKey
|
|
RDEPUBWebViewDebug.log(debugScope, message: "configured webView=\(RDEPUBWebViewDebug.webViewID(webView)) publicationKey=\(publicationKey)")
|
|
}
|
|
|
|
func handleSelectionMenuAction(_ action: RDEPUBAnnotationMenuAction) {
|
|
delegate?.epubWebView(self, didRequestSelectionAction: action)
|
|
clearWebSelection()
|
|
}
|
|
|
|
func clearWebSelection() {
|
|
webView?.evaluateJavaScript("window.getSelection && window.getSelection().removeAllRanges();")
|
|
}
|
|
|
|
func teardownWebView() {
|
|
cancelFixedLayoutReadyFallback()
|
|
if let webView {
|
|
RDEPUBWebViewDebug.log(debugScope, message: "teardown webView=\(RDEPUBWebViewDebug.webViewID(webView))")
|
|
}
|
|
// M-15: Clean up UIEditMenuInteraction on iOS 16+
|
|
if #available(iOS 16.0, *), let interaction = (webView as? RDEPUBAnnotationWebView)?._editMenuInteraction as? UIEditMenuInteraction {
|
|
webView?.removeInteraction(interaction)
|
|
}
|
|
webView?.navigationDelegate = nil
|
|
if let userContentController = webView?.configuration.userContentController {
|
|
RDEPUBJavaScriptBridge.messageNames.forEach {
|
|
userContentController.removeScriptMessageHandler(forName: $0)
|
|
}
|
|
}
|
|
(webView as? RDEPUBAnnotationWebView)?.onSelectionAction = nil
|
|
webView?.removeFromSuperview()
|
|
webView = nil
|
|
schemeHandler = nil
|
|
configuredPublicationKey = nil
|
|
currentLoadSignature = nil
|
|
}
|
|
}
|