import UIKit protocol RDEPUBWebContentViewDelegate: AnyObject { func epubWebContentView(_ contentView: RDEPUBWebContentView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int) 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) func epubWebContentView(_ contentView: RDEPUBWebContentView, didLogJavaScriptError message: String) func epubWebContentView(_ contentView: RDEPUBWebContentView, didTapImageWithSource src: String, sourceRect: CGRect?) func epubWebContentView(_ contentView: RDEPUBWebContentView, didTapFootnoteWithAltText altText: String, sourceRect: CGRect?) } final class RDEPUBWebContentView: UIView, RDEpubReaderPageResourceReleasing, RDEpubReaderPageVisibilityHandling { weak var delegate: RDEPUBWebContentViewDelegate? /// The current href of the loaded chapter (exposed for image resolution). var currentHref: String { epubWebView.currentHref } 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) } } func readerPageVisibilityDidChange(isVisible: Bool) { epubWebView.setReaderPageVisible(isVisible) } } 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) {} func epubWebView(_ webView: RDEPUBWebView, didTapImageWithSource src: String, sourceRect: CGRect?) { delegate?.epubWebContentView(self, didTapImageWithSource: src, sourceRect: sourceRect) } func epubWebView(_ webView: RDEPUBWebView, didTapFootnoteWithAltText altText: String, sourceRect: CGRect?) { delegate?.epubWebContentView(self, didTapFootnoteWithAltText: altText, sourceRect: sourceRect) } } extension RDEPUBWebContentViewDelegate { func epubWebContentView(_ contentView: RDEPUBWebContentView, didTapImageWithSource src: String, sourceRect: CGRect?) { // Default: no-op. Implementors can present an image viewer. } func epubWebContentView(_ contentView: RDEPUBWebContentView, didTapFootnoteWithAltText altText: String, sourceRect: CGRect?) { // Default: no-op. Implementors can present a footnote tooltip. } }