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

如何禁用UITextView中Look up、Share等不需要的系统文本菜单选项

解决方案

拦截失效有两个核心原因:

  1. 你用Extension重写UITextView的canPerformAction本身是错误实现:分类/扩展无法可靠替换系统类的原生方法,方法加载顺序不确定,大概率会被系统实现覆盖,不会执行你写的逻辑,还会全局污染所有UITextView实例。
  2. 拦截的Selector不全、名称不匹配:你只写了_lookup:和_share:,漏了不同系统版本下这几个功能对应的变体Selector,加上系统菜单会沿响应链逐层判断是否可执行,只在当前层返回false不足以完全拦截。

具体实现步骤

  1. 创建UITextView的自定义子类,不要用扩展重写系统方法
  2. 补全所有需要拦截的系统菜单对应的Selector,覆盖新旧版本系统的方法名
  3. iOS 17及以上版本优先用UIMenuBuilder从构建层移除不需要的菜单,稳定性远高于Selector拦截

兼容低版本的Selector拦截代码

class MenuFilteredTextView: UITextView {
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        // 要禁用的菜单对应的Selector列表,覆盖全系统版本
        let blockedActions = [
            Selector(("_lookup:")),
            Selector(("_lookUp:")),
            Selector(("_define:")),
            Selector(("_share:")),
            Selector(("_showSpellingPanel:")),
            Selector(("_guess:")),
            Selector(("_checkSpelling:")),
            Selector(("_speak:")),
            Selector(("_pauseSpeaking:")),
            Selector(("_stopSpeaking:"))
        ]
        if blockedActions.contains(action) {
            return false
        }

        // 仅保留需要显示的菜单
        let allowedActions = [
            #selector(UIResponderStandardEditActions.copy(_:)),
            #selector(copyAll(_:)),
            #selector(lookUpWord(_:))
        ]
        return allowedActions.contains(action)
    }
}

iOS 17+ 更稳定的菜单构建拦截

override func buildMenu(with builder: UIMenuBuilder) {
    // 直接移除对应系统菜单组,不需要逐个判断Selector
    builder.remove(menu: .lookup)
    builder.remove(menu: .share)
    builder.remove(menu: .spelling)
    builder.remove(menu: .speech)
    super.buildMenu(with: builder)
}

这个实现是在系统生成菜单阶段直接删除不需要的菜单项,不会出现返回false但选项依然显示的问题,没有响应链穿透的隐患,只要系统版本满足就优先使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 21:57:23