You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:30:58