- RDPDFReaderPageView:完整页面视图移入 SDK(缩放画布、文字选择层、 选区放大镜、无障碍视口),并内置与 EPUB 一致的两套系统编辑菜单: 选区菜单(复制/划线/注释)、已有划线菜单(复制/注释|删除注释/删除划线) - RDPDFReaderPageViewDelegate:宿主只通过代理做数据持久化与打开业务界面 - 修复划线命中判定:点击点先归一化再与标注比例坐标比较 - 修复 tappedHighlight 残留导致选区菜单项被错误过滤的问题 - 主题应用改为纯颜色入参,SDK 不依赖 EPUB 主题类型 - RDPDFReaderHighlightOverlayView 恢复为纯绘制层,点击交互统一由页面视图处理 - Demo 删除 PDFDemoPageView(约 290 行),改为实现代理回调 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
3.0 KiB
Swift
68 lines
3.0 KiB
Swift
import UIKit
|
||
|
||
/// SDK 标注覆盖层:只负责以比例坐标绘制已保存的划线。
|
||
/// 划线的点击与菜单交互统一由 `RDPDFReaderPageView` 处理。
|
||
public final class RDPDFReaderHighlightOverlayView: UIView {
|
||
public var highlights: [RDPDFReaderHighlight] = [] { didSet { setNeedsDisplay() } }
|
||
|
||
public override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
isOpaque = false
|
||
backgroundColor = .clear
|
||
isUserInteractionEnabled = false
|
||
}
|
||
|
||
public required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
public override func draw(_ rect: CGRect) {
|
||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||
context.clear(rect)
|
||
for highlight in highlights {
|
||
let color = UIColor(hexString: highlight.color)
|
||
context.setStrokeColor(color.cgColor)
|
||
context.setLineWidth(2)
|
||
let normalizedRect = highlight.normalizedRect
|
||
let underlineRect = CGRect(x: rect.width * normalizedRect.minX, y: rect.height * normalizedRect.minY, width: rect.width * normalizedRect.width, height: rect.height * normalizedRect.height)
|
||
let y = underlineRect.maxY - 1
|
||
context.move(to: CGPoint(x: underlineRect.minX, y: y))
|
||
context.addLine(to: CGPoint(x: underlineRect.maxX, y: y))
|
||
context.strokePath()
|
||
}
|
||
}
|
||
}
|
||
|
||
/// SDK 搜索命中覆盖层。与读者标注共用页面比例坐标体系。
|
||
public final class RDPDFReaderSearchHighlightOverlayView: UIView {
|
||
public var matchRects: [CGRect] = [] { didSet { setNeedsDisplay() } }
|
||
public var activeIndex = -1 { didSet { setNeedsDisplay() } }
|
||
|
||
public override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
isOpaque = false
|
||
backgroundColor = .clear
|
||
isUserInteractionEnabled = false
|
||
}
|
||
|
||
public required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
public override func draw(_ rect: CGRect) {
|
||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||
context.clear(rect)
|
||
for (index, matchRect) in matchRects.enumerated() {
|
||
let color = index == activeIndex
|
||
? UIColor(red: 0.14, green: 0.42, blue: 0.95, alpha: 0.34)
|
||
: UIColor(red: 0.21, green: 0.48, blue: 0.95, alpha: 0.16)
|
||
context.setFillColor(color.cgColor)
|
||
context.fill(CGRect(x: rect.width * matchRect.minX, y: rect.height * matchRect.minY, width: rect.width * matchRect.width, height: rect.height * matchRect.height))
|
||
}
|
||
}
|
||
}
|
||
|
||
private extension UIColor {
|
||
convenience init(hexString: String) {
|
||
let hex = hexString.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "#", with: "")
|
||
let value = UInt32(hex, radix: 16) ?? 0
|
||
self.init(red: CGFloat((value >> 16) & 0xFF) / 255, green: CGFloat((value >> 8) & 0xFF) / 255, blue: CGFloat(value & 0xFF) / 255, alpha: 1)
|
||
}
|
||
}
|