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

iOS(Swift 5):如何编程检测文本菜单操作及获取选中内容

监听UITextView文本菜单操作并获取选中内容(Swift实现)

核心思路

通过替换系统默认文本菜单的操作绑定,自定义响应逻辑来捕获点击时机,同时在响应方法中直接获取当前选中的文本内容。

实现步骤

1. 自定义UITextView子类

创建子类重写canPerformAction(_:withSender:)方法,筛选需要监听的系统操作,替换为自定义响应逻辑:

import UIKit

class TrackableTextView: UITextView {
    // 操作回调,传递操作类型和选中内容
    var menuActionCallback: ((String, String?) -> Void)?
    
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        // 定义需要监听的系统操作Selector
        let trackedActions: [Selector] = [
            #selector(copy(_:)),
            #selector(cut(_:)),
            #selector(selectAll(_:)),
            Selector(("_translate:"))
        ]
        
        if trackedActions.contains(action) {
            // 隐藏默认菜单,替换为自定义响应
            UIMenuController.shared.setMenuVisible(false, animated: false)
            
            let actionTitle = UIMenuController.shared.menuItems?.first { $0.action == action }?.title ?? ""
            let customAction = UIAction(title: actionTitle) { [weak self] _ in
                guard let self = self else { return }
                let selectedText = self.selectedTextRange.flatMap { self.text(in: $0) }
                var actionType = ""
                
                switch action {
                case #selector(copy(_:)):
                    actionType = "copy"
                    super.copy(sender) // 保留系统默认复制行为
                case #selector(cut(_:)):
                    actionType = "cut"
                    super.cut(sender) // 保留系统默认剪切行为
                case #selector(selectAll(_:)):
                    actionType = "select all"
                    super.selectAll(sender) // 保留系统默认全选行为
                case Selector(("_translate:")):
                    actionType = "translate"
                    // 系统翻译为私有API,若需保留原功能需调用私有方法,注意审核风险
                default:
                    break
                }
                
                self.menuActionCallback?(actionType, selectedText)
            }
            
            let customMenuItem = UIMenuItem(title: customAction.title, action: action)
            UIMenuController.shared.menuItems = [customMenuItem]
            UIMenuController.shared.setMenuVisible(true, animated: true)
            return false
        }
        
        return super.canPerformAction(action, withSender: sender)
    }
    
    // 单独处理"Look Up"操作(对应系统私有Selector _define:)
    override func _define(_ sender: Any?) {
        let selectedText = selectedTextRange.flatMap { text(in: $0) }
        menuActionCallback?("look up", selectedText)
        super._define(sender) // 保留系统默认查询行为
    }
}

2. 在ViewController中使用并监听回调

初始化自定义TextView,设置回调处理操作事件:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let textView = TrackableTextView(frame: view.bounds.inset(by: UIEdgeInsets(top: 100, left: 20, bottom: 100, right: 20)))
        textView.text = "测试文本,用于监听copy、cut、translate等菜单操作"
        textView.font = .systemFont(ofSize: 16)
        textView.backgroundColor = .white
        
        // 监听菜单操作回调
        textView.menuActionCallback = { actionType, selectedContent in
            print("操作类型:\(actionType),选中内容:\(selectedContent ?? "无")")
            // 此处可添加日志上报、业务逻辑处理等操作
        }
        
        view.addSubview(textView)
    }
}

注意事项

  • 系统translate和look up对应的Selector为私有API,使用时需注意App Store审核风险;若要规避风险,可完全自定义文本菜单,不依赖系统私有方法。
  • 自定义响应逻辑中必须手动调用super对应的方法,否则会丢失系统默认的菜单功能。
  • 全选操作的回调中,selectedText为操作前的选中内容;若需获取全选后的内容,可延迟执行获取逻辑(如使用DispatchQueue.main.async)。

内容的提问来源于stack exchange,提问作者user8460166

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:51:20