如何为iOS PDFKit的文本选择添加新操作并自定义弹出菜单
iOS PDFKit 文本选择菜单自定义方案
核心实现逻辑
PDFKit的文本选择菜单基于UIKit的UIMenuController(iOS 15及以下)和UIEditMenuInteraction(iOS 16+)体系,而非PDFView专属协议。实现自定义需从两个核心方向入手:注入自定义操作、替换/重写菜单UI。
添加自定义操作步骤
- 自定义
PDFView子类,重写canPerformAction(_:withSender:)方法,控制自定义操作的可用性(仅当存在文本选择时返回true) - 在子类中实现自定义操作的处理函数,与系统默认操作(如
copy(_:))同层级 - 注册自定义
UIMenuItem(iOS 15-)或通过UIEditMenuInteractionDelegate注入菜单项(iOS 16+)
自定义菜单UI思路
- 若仅需修改菜单项样式:iOS 14+支持给
UIMenuItem设置image属性,替换默认图标 - 若需完全自定义菜单:在
selectionChanged(_:)回调中隐藏系统菜单,利用2D图形知识自行绘制弹窗(通过UIView/CALayer实现),并绑定文本选择的操作逻辑,注意处理PDF坐标到View坐标的转换,确保弹窗定位准确
关键代码示例
自定义PDFView子类(兼容iOS 15及以下)
class CustomPDFView: PDFView { private let customMenuItem = UIMenuItem(title: "自定义操作", action: #selector(handleCustomAction(_:))) override func awakeFromNib() { super.awakeFromNib() // 注册自定义菜单项 UIMenuController.shared.menuItems = (UIMenuController.shared.menuItems ?? []) + [customMenuItem] } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { if action == #selector(handleCustomAction(_:)) { return currentSelection != nil } // 保留系统默认操作 return super.canPerformAction(action, withSender: sender) } override func selectionChanged(_ notification: Notification) { super.selectionChanged(notification) // 此处可添加自定义菜单的显示/隐藏逻辑 if currentSelection == nil { hideCustomMenuIfNeeded() } } @objc private func handleCustomAction(_ sender: Any?) { guard let selectedText = currentSelection?.string else { return } // 执行自定义业务逻辑 print("选中文本:\(selectedText)") } private func hideCustomMenuIfNeeded() { // 移除自定义菜单视图(若有) subviews.forEach { if $0.tag == 1001 { $0.removeFromSuperview() } } } }
iOS 16+ 适配代码(使用UIEditMenuInteraction)
extension CustomPDFView: UIEditMenuInteractionDelegate { override func viewDidLoad() { super.viewDidLoad() let editMenuInteraction = UIEditMenuInteraction(delegate: self) addInteraction(editMenuInteraction) } func editMenuInteraction(_ interaction: UIEditMenuInteraction, menuFor configuration: UIEditMenuConfiguration, suggestedActions: [UIMenuElement]) -> UIMenu? { let customAction = UIAction(title: "自定义操作", image: UIImage(systemName: "star")) { [weak self] _ in guard let selectedText = self?.currentSelection?.string else { return } // 执行自定义逻辑 print("选中文本:\(selectedText)") } // 将自定义操作插入到系统菜单中 let modifiedActions = suggestedActions + [customAction] return UIMenu(title: "", children: modifiedActions) } }
完全自定义菜单UI示例(简化版)
private func showCustomMenu() { guard let selectionBounds = currentSelection?.bounds(for: self) else { return } // 转换PDF坐标到View坐标 let viewBounds = convert(selectionBounds, from: documentView) // 创建自定义菜单视图 let menuView = UIView(frame: CGRect(x: viewBounds.midX - 100, y: viewBounds.minY - 50, width: 200, height: 44)) menuView.tag = 1001 menuView.backgroundColor = .white menuView.layer.cornerRadius = 8 menuView.layer.shadowColor = UIColor.black.cgColor menuView.layer.shadowOpacity = 0.3 menuView.layer.shadowOffset = CGSize(width: 0, height: 2) let actionBtn = UIButton(type: .system) actionBtn.setTitle("自定义操作", for: .normal) actionBtn.addTarget(self, action: #selector(handleCustomAction(_:)), for: .touchUpInside) actionBtn.frame = menuView.bounds menuView.addSubview(actionBtn) addSubview(menuView) }
内容的提问来源于stack exchange,提问作者user3982143
相关产品推荐
相关产品推荐

