如何在SwiftUI中从UITextViewRepresentable的textViewDidChangeSelection代理方法获取选中范围并传递至ContentView
Got it, let's solve this problem of passing the UITextView's selected range from the textViewDidChangeSelection delegate method up to your SwiftUI ContentView. Here's how you can do it step by step:
Step 1: Add a Binding for Selected Range in UITextViewRepresentable
First, we'll add a @Binding property to hold the selected range, which will let us sync the value between UIKit and SwiftUI. Also, fix the UITextView initialization (don't declare it as a stored property on the struct—create it inside makeUIView to avoid reuse issues).
Step 2: Update the Coordinator to Sync Selected Range
Pass the new selected range binding to the Coordinator, then update it whenever the selection changes in textViewDidChangeSelection.
Step 3: Use the Selected Range in ContentView
Add a @State variable to track the selected range in your ContentView, and pass it to the UITextViewRepresentable.
Complete Modified Code
struct ContentView: View { @State private var text = "" @State private var selectedRange = NSRange(location: 0, length: 0) // Track selected range here var body: some View { VStack { UITextViewRepresentable(text: $text, selectedRange: $selectedRange) // Show the selected range in SwiftUI Text("Selected Range: Location \(selectedRange.location), Length \(selectedRange.length)") .padding() } } } struct UITextViewRepresentable: UIViewRepresentable { @Binding var text: String @Binding var selectedRange: NSRange // New binding for selected range func makeUIView(context: Context) -> UITextView { let textView = UITextView() // Create textView here instead of stored property textView.delegate = context.coordinator return textView } func updateUIView(_ uiView: UITextView, context: Context) { // Only update text if it's different to avoid overwriting selection if uiView.text != text { uiView.text = text } // Optional: Sync selection from SwiftUI to UIKit if needed // if uiView.selectedRange != selectedRange { // uiView.selectedRange = selectedRange // } } func makeCoordinator() -> Coordinator { Coordinator(text: $text, selectedRange: $selectedRange) // Pass selectedRange binding } class Coordinator: NSObject, UITextViewDelegate { @Binding var text: String @Binding var selectedRange: NSRange // Hold the binding init(text: Binding<String>, selectedRange: Binding<NSRange>) { self._text = text self._selectedRange = selectedRange } func textViewDidChange(_ textView: UITextView) { text = textView.text ?? "" } func textViewDidChangeSelection(_ textView: UITextView) { // Update the binding with the new selected range selectedRange = textView.selectedRange print("Selected Range: \(textView.selectedRange)") } } }
Key Notes:
- We added a check in
updateUIViewto only update the text if it's different—this prevents overwriting the user's selection when SwiftUI updates the view. - The
selectedRangebinding flows both ways: if you update it in SwiftUI, you can sync it back to the UITextView (uncomment the optional code inupdateUIViewif you need that). - Now your
ContentViewhas access to the current selected range, which you can use for any UI updates or logic.
内容的提问来源于stack exchange,提问作者Dev Rana

