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:
parent
7c16647306
commit
5a19cfde14
118
Sources/RDPDFReaderView/Sources/RDPDFReaderPageView.swift
Normal file
118
Sources/RDPDFReaderView/Sources/RDPDFReaderPageView.swift
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user