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

UIMenuController仅保留Copy和Share功能异常求助

解决UITextView自定义菜单的两个问题:Copy不显示、Share位置错误

先来说说你的两个问题的根源,以及对应的修复方案:

问题1:仅显示Share按钮,Copy不出现

原因分析

  1. 自定义长按手势干扰默认文本选择:你给UITextView加了自定义的UILongPressGestureRecognizer,但UITextView本身自带长按选中文本的交互逻辑,自定义手势会抢占响应,导致系统检测不到文本被选中——而Copy按钮只有在存在选中内容时才会激活显示。
  2. 错误的Selector写法:你用了Selector("_copy:")和Selector("_share:")这类私有API写法,正确的系统公开选择器应该是#selector(copy(_:))(对应Copy)和#selector(UIResponderStandardEditActions.share(_:))(对应Share)。
  3. 多余的UIScrollView扩展:UIScrollView并不是第一响应者(第一响应者是UITextView),给它重写canPerformAction完全没必要,还可能引发逻辑冲突。

修复步骤

  • 删掉自定义的长按手势相关代码(包括viewDidLoad里的手势创建、添加,以及longPressHandler方法)。
  • 修正UITextView的canPerformAction扩展,用正确的选择器,同时确保Copy仅在有文本选中时才允许:
extension UITextView {
    override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        // 只有选中文本时才允许Copy
        let allowCopy = action == #selector(copy(_:)) && self.selectedRange.length > 0
        let allowShare = action == #selector(UIResponderStandardEditActions.share(_:))
        return allowCopy || allowShare
    }
}

问题2:Share按钮显示在屏幕中心而非选中文本位置

原因分析

你之前通过自定义长按手势设置setTargetRect(senderView.frame, in: superView),这里的senderView.frame是整个UITextView的范围,而非选中文本的区域,所以菜单会显示在整个TextView的位置,看起来像是屏幕中心。

修复步骤

既然我们移除了自定义长按手势,现在依赖UITextView的默认文本选择交互,系统会自动处理菜单位置。如果需要更精准的控制,可以重写UITextView的menuTargetRect(for:)方法指定菜单目标区域:

extension UITextView {
    override open func menuTargetRect(for configuration: UIContextMenuConfiguration) -> CGRect {
        guard let selectedRange = self.selectedTextRange else { 
            return super.menuTargetRect(for: configuration) 
        }
        // 获取选中文本的第一个矩形区域
        let textRect = self.firstRect(for: selectedRange)
        // 转换到当前视图坐标系
        return self.convert(textRect, from: self.textInputView)
    }
}

完整修正后的代码

import UIKit

class DetailViewController: UIViewController {
    var selectedStory : URL!
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var textSlider: UISlider! {
        didSet {
            configureSlider()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        let storyText = try? String(contentsOf: selectedStory)
        textView.text = storyText
        textView.isUserInteractionEnabled = true
        textView.isSelectable = true // 确保文本可选中,即使不需要编辑
        // 移除了自定义长按手势
    }

    override var canBecomeFirstResponder: Bool {
        return true
    }
    
    // 你的滑块配置方法
    func configureSlider() {
        // 这里放滑块的配置逻辑
    }
}

extension UITextView {
    override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        let allowCopy = action == #selector(copy(_:)) && self.selectedRange.length > 0
        let allowShare = action == #selector(UIResponderStandardEditActions.share(_:))
        return allowCopy || allowShare
    }
    
    override open func menuTargetRect(for configuration: UIContextMenuConfiguration) -> CGRect {
        guard let selectedRange = self.selectedTextRange else { 
            return super.menuTargetRect(for: configuration) 
        }
        let textRect = self.firstRect(for: selectedRange)
        return self.convert(textRect, from: self.textInputView)
    }
}

额外提醒

如果不需要文本编辑功能,可以设置textView.isEditable = false,只要isSelectable = true,依然可以选中文本并显示菜单。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:20:18