121 lines
4.9 KiB
Swift
121 lines
4.9 KiB
Swift
import UIKit
|
||
|
||
// MARK: - Web 内容视图代理
|
||
|
||
/// Web 内容视图的代理协议
|
||
/// 将 WebView 中的事件(位置更新、文本选择、链接点击等)转发给控制器
|
||
protocol RDEPUBWebContentViewDelegate: AnyObject {
|
||
/// WebView 中阅读位置更新时调用
|
||
func epubWebContentView(_ contentView: RDEPUBWebContentView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int)
|
||
/// WebView 中文本选择变化时调用
|
||
func epubWebContentView(_ contentView: RDEPUBWebContentView, didChangeSelection selection: RDEPUBSelection?, spineIndex: Int)
|
||
/// 用户从选择菜单中触发操作
|
||
func epubWebContentView(_ contentView: RDEPUBWebContentView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction)
|
||
/// 用户点击内部链接(章节跳转)
|
||
func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateInternalLink location: RDEPUBLocation, fromSpineIndex: Int)
|
||
/// 用户点击外部链接
|
||
func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateExternalLink url: URL)
|
||
/// WebView 中 JavaScript 执行出错
|
||
func epubWebContentView(_ contentView: RDEPUBWebContentView, didLogJavaScriptError message: String)
|
||
}
|
||
|
||
// MARK: - Web 内容视图
|
||
|
||
/// EPUB 固定布局和 Web 渲染路径的内容视图
|
||
/// 包装 RDEPUBWebView,提供页码标签和加载/释放资源的控制
|
||
/// 适用于固定布局 EPUB 和不支持 Native Text 的场景
|
||
final class RDEPUBWebContentView: UIView {
|
||
weak var delegate: RDEPUBWebContentViewDelegate?
|
||
|
||
private let epubWebView = RDEPUBWebView()
|
||
private let decorationOverlayView = RDEPUBWebDecorationOverlayView()
|
||
private let pageNumberLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.font = UIFont.systemFont(ofSize: 13)
|
||
return label
|
||
}()
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
clipsToBounds = true
|
||
layer.masksToBounds = true
|
||
|
||
addSubview(epubWebView)
|
||
addSubview(decorationOverlayView)
|
||
addSubview(pageNumberLabel)
|
||
epubWebView.delegate = self
|
||
epubWebView.onDecorationsResolved = { [weak self] decorations in
|
||
self?.decorationOverlayView.applyDecorations(decorations)
|
||
}
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
epubWebView.frame = bounds
|
||
decorationOverlayView.frame = bounds
|
||
|
||
let labelSize = pageNumberLabel.sizeThatFits(CGSize(width: bounds.width, height: 20))
|
||
pageNumberLabel.frame = CGRect(
|
||
x: bounds.width - labelSize.width - 24,
|
||
y: bounds.height - labelSize.height - 20,
|
||
width: labelSize.width,
|
||
height: labelSize.height
|
||
)
|
||
}
|
||
|
||
func configure(
|
||
publication: RDEPUBPublication,
|
||
request: RDEPUBRenderRequest,
|
||
pageNumber: Int,
|
||
totalPages: Int,
|
||
theme: RDEPUBReaderTheme
|
||
) {
|
||
backgroundColor = theme.contentBackgroundColor
|
||
pageNumberLabel.textColor = theme.contentTextColor
|
||
pageNumberLabel.text = "\(pageNumber) / \(totalPages)"
|
||
decorationOverlayView.applyDecorations([])
|
||
epubWebView.load(publication: publication, request: request)
|
||
setNeedsLayout()
|
||
}
|
||
|
||
func releaseResources() {
|
||
decorationOverlayView.applyDecorations([])
|
||
epubWebView.reset()
|
||
epubWebView.delegate = self
|
||
epubWebView.onDecorationsResolved = { [weak self] decorations in
|
||
self?.decorationOverlayView.applyDecorations(decorations)
|
||
}
|
||
}
|
||
}
|
||
|
||
extension RDEPUBWebContentView: RDEPUBWebViewDelegate {
|
||
func epubWebView(_ webView: RDEPUBWebView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int) {
|
||
delegate?.epubWebContentView(self, didUpdateLocation: location, spineIndex: spineIndex)
|
||
}
|
||
|
||
func epubWebView(_ webView: RDEPUBWebView, didChangeSelection selection: RDEPUBSelection?, spineIndex: Int) {
|
||
delegate?.epubWebContentView(self, didChangeSelection: selection, spineIndex: spineIndex)
|
||
}
|
||
|
||
func epubWebView(_ webView: RDEPUBWebView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction) {
|
||
delegate?.epubWebContentView(self, didRequestSelectionAction: action)
|
||
}
|
||
|
||
func epubWebView(_ webView: RDEPUBWebView, didActivateInternalLink location: RDEPUBLocation, fromSpineIndex: Int) {
|
||
delegate?.epubWebContentView(self, didActivateInternalLink: location, fromSpineIndex: fromSpineIndex)
|
||
}
|
||
|
||
func epubWebView(_ webView: RDEPUBWebView, didActivateExternalLink url: URL) {
|
||
delegate?.epubWebContentView(self, didActivateExternalLink: url)
|
||
}
|
||
|
||
func epubWebView(_ webView: RDEPUBWebView, didLogJavaScriptError message: String) {
|
||
delegate?.epubWebContentView(self, didLogJavaScriptError: message)
|
||
}
|
||
func epubWebViewDidFinishRendering(_ webView: RDEPUBWebView) {}
|
||
}
|