Как получить выделенный текст внутри TextEditor в SwiftUI на macOS
TextEditor в SwiftUI все еще не имеет API для получения выделения пользователя. Но так как внутри используется NSTextView, мы можем подписаться на его уведомления.
Вот пример того, как получить подстроку из пользовательского выделения:
До macOS 15:
import SwiftUI
struct ContentView: View {
@State var myString = "Привет, мир"
@State var selectedString = ""
var body: some View {
HStack {
Text("Выделение:")
.foregroundStyle(.secondary)
Text("\"\(selectedString)\"")
}.padding()
TextEditor(text: $myString)
.font(.title)
.onReceive(NotificationCenter.default.publisher(
for: NSTextView.didChangeSelectionNotification
), perform: { notification in
guard let textView = notification.object as? NSTextView else { return }
// Проверяем, тот ли это текст, который нам нужен
guard textView.string == myString else { return }
let selectionRage = textView.selectedRange()
let start = String.Index(
utf16Offset: selectionRage.lowerBound, in: myString
)
let end = String.Index(
utf16Offset: selectionRage.upperBound, in: myString
)
let subStringRange: Range<String.Index> = start..<end
selectedString = String(myString[subStringRange])
})
}
}
На macOS 15+ появилась новая структура TextSelection, которую можно использовать в подписчике .onChange:
import SwiftUI
struct ContentView: View {
@State var myString = "Привет, мир"
@SSState var selectedString = ""
@State private var selection: TextSelection?
var body: some View {
HStack {
Text("Выделение:")
.foregroundStyle(.secondary)
Text("\"\(selectedString)\"")
}.padding()
TextEditor(text: $myString, selection: $selection)
.font(.title)
.onChange(of: selection) {
if let selection = selection.take() {
if case let .selection(range) = selection.indices {
selectedString = String(myString[range])
}
}
}
}
}
// ─── EOF ──────────────────────────────────────────────────