SwiftUI中如何检测文本选择事件并触发ContextMenu
SwiftUI原生Text视图无法直接监听文本选择事件并获取选中内容,最可靠的方式是通过UIViewRepresentable包装UITextView,利用UIKit的代理机制实现需求。以下是具体实现步骤:
1. 实现UITextView的SwiftUI包装器
这个包装器会处理文本选择监听、选中内容传递,并支持延迟显示ContextMenu:
import SwiftUI import UIKit struct SelectableText: UIViewRepresentable { let text: String let font: UIFont let textColor: UIColor @Binding var selectedText: String var onSelectionChanged: (String) -> Void func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.text = text textView.font = font textView.textColor = textColor textView.isEditable = false textView.isSelectable = true textView.delegate = context.coordinator // 移除默认菜单,自定义延迟显示逻辑 textView.inputAssistantItem.leadingBarButtonGroups = [] textView.inputAssistantItem.trailingBarButtonGroups = [] return textView } func updateUIView(_ uiView: UITextView, context: Context) { uiView.text = text uiView.font = font uiView.textColor = textColor } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } class Coordinator: NSObject, UITextViewDelegate { let parent: SelectableText private var selectionTimer: Timer? init(parent: SelectableText) { self.parent = parent } func textViewDidChangeSelection(_ textView: UITextView) { // 清除之前的定时器,避免重复触发 selectionTimer?.invalidate() guard let selectedRange = textView.selectedTextRange, let selectedText = textView.text(in: selectedRange), !selectedText.isEmpty else { parent.selectedText = "" return } // 延迟500毫秒触发菜单显示和内容回调 selectionTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in guard let self = self else { return } self.parent.selectedText = selectedText self.parent.onSelectionChanged(selectedText) // 显示自定义ContextMenu let menu = UIMenu(title: "", children: [ UIAction(title: "复制", handler: { _ in UIPasteboard.general.string = selectedText }), UIAction(title: "分享", handler: { _ in // 在这里添加自定义分享逻辑 }) ]) textView.showContextMenu(menu, at: textView.caretRect(for: selectedRange.end), in: textView) } } } }
2. 在SwiftUI视图中使用包装器
将SelectableText嵌入你的SwiftUI界面,绑定选中内容并处理回调:
struct ContentView: View { @State private var selectedText = "" var body: some View { VStack(spacing: 20) { SelectableText( text: "这是一段可以选中的文本,选中文本后会延迟显示自定义菜单,并获取选中内容。", font: .systemFont(ofSize: 16), textColor: .label, selectedText: $selectedText, onSelectionChanged: { text in print("选中的内容:\(text)") // 在这里添加你的自定义处理逻辑 } ) .padding() Text("当前选中:\(selectedText.isEmpty ? "无" : selectedText)") .foregroundColor(.secondary) } .padding() } }
关键说明
- 原生选择支持:通过
UITextView的isSelectable = true启用原生文本选择交互,完全无需自定义点击逻辑。 - 选中内容监听:利用
textViewDidChangeSelection代理方法实时获取选中范围和内容,通过@Binding或回调传递给SwiftUI视图。 - 延迟显示菜单:使用
Timer实现延迟触发,避免用户拖动选择时频繁弹出菜单,提升交互流畅度。 - 自定义ContextMenu:替换系统默认菜单,你可以根据需求添加任意自定义操作,若想保留系统菜单,只需移除清空
inputAssistantItem的代码即可。
这个方案基于Apple原生组件实现,稳定性和交互体验都有保障,完美解决SwiftUI原生Text无法监听选择事件的问题。
内容的提问来源于stack exchange,提问作者Mustafa Girgin
相关产品推荐
相关产品推荐

