refactor: move highlight menu logic from Demo to SDK library

Add RDPDFReaderPageView as base page view class in SDK:
- Handles all highlight menu interactions and display
- Provides RDPDFReaderPageViewDelegate for menu action callbacks
- Unifies with EPUB architecture: RDEPUBTextContentView pattern
- Reduces code duplication for applications using the SDK

Benefits:
- Reusable menu interaction logic across all PDF apps
- Consistent architecture with EPUB reader
- Single responsibility principle (UI in library, persistence in app)
- Backward compatible (existing code continues to work)

Applications now implement RDPDFReaderPageViewDelegate to:
- Respond to highlight menu actions
- Handle data persistence
- Refresh UI after changes

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
shenlei 2026-07-15 10:19:54 +09:00
parent 7c16647306
commit 5a19cfde14

View File

@ -0,0 +1,118 @@
import UIKit
/// PDF
/// EPUB RDEPUBTextContentView
open class RDPDFReaderPageView: UIView, UIGestureRecognizerDelegate {
public weak var delegate: RDPDFReaderPageViewDelegate?
/// 宿PDF
public let contentView = UIView()
/// contentView
public let textLayer = RDPDFReaderImageTextLayerView()
private var tappedHighlight: RDPDFReaderAnnotation?
public override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
private func setupViews() {
addSubview(contentView)
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
textLayer.frame = contentView.bounds
textLayer.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contentView.addSubview(textLayer)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
addGestureRecognizer(tapGesture)
}
@objc private func handleTap(_ gesture: UITapGestureRecognizer) {
let point = gesture.location(in: textLayer)
if let highlight = highlightAt(point) {
showExistingHighlightMenu(for: highlight, at: gesture.location(in: self))
return
}
}
private func highlightAt(_ point: CGPoint) -> RDPDFReaderAnnotation? {
let currentPageAnnotations = textLayer.annotations.filter { $0.pageIndex == textLayer.pageIndex }
for annotation in currentPageAnnotations {
for rect in annotation.normalizedRects {
if rect.contains(point) {
return annotation
}
}
}
return nil
}
private func showExistingHighlightMenu(for annotation: RDPDFReaderAnnotation, at point: CGPoint) {
tappedHighlight = annotation
becomeFirstResponder()
var menuItems: [UIMenuItem] = [
UIMenuItem(title: "复制", action: #selector(rd_copy_highlight(_:)))
]
if annotation.note?.isEmpty == false {
menuItems.append(UIMenuItem(title: "删除注释", action: #selector(rd_delete_annotation(_:))))
} else {
menuItems.append(UIMenuItem(title: "注释", action: #selector(rd_annotate_highlight(_:))))
}
menuItems.append(UIMenuItem(title: "删除划线", action: #selector(rd_delete_highlight(_:))))
let menuController = UIMenuController.shared
menuController.menuItems = menuItems
menuController.setTargetRect(CGRect(x: point.x, y: point.y, width: 1, height: 1), in: self)
menuController.setMenuVisible(true, animated: true)
}
override open var canBecomeFirstResponder: Bool { true }
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if tappedHighlight != nil {
return action == #selector(rd_copy_highlight(_:))
|| action == #selector(rd_delete_highlight(_:))
|| action == #selector(rd_annotate_highlight(_:))
|| action == #selector(rd_delete_annotation(_:))
}
return false
}
@objc private func rd_copy_highlight(_ sender: Any?) {
guard let highlight = tappedHighlight else { return }
delegate?.pageView(self, didRequestHighlightMenuAction: .copy, highlight: highlight)
tappedHighlight = nil
}
@objc private func rd_annotate_highlight(_ sender: Any?) {
guard let highlight = tappedHighlight else { return }
delegate?.pageView(self, didRequestHighlightMenuAction: .annotate, highlight: highlight)
tappedHighlight = nil
}
@objc private func rd_delete_annotation(_ sender: Any?) {
guard let highlight = tappedHighlight else { return }
delegate?.pageView(self, didRequestHighlightMenuAction: .deleteAnnotation, highlight: highlight)
tappedHighlight = nil
}
@objc private func rd_delete_highlight(_ sender: Any?) {
guard let highlight = tappedHighlight else { return }
delegate?.pageView(self, didRequestHighlightMenuAction: .deleteUnderline, highlight: highlight)
tappedHighlight = nil
}
}