TechnologiesSwiftUI

TextSelection struct

iOSmacOStvOSwatchOSvisionOSiOS 18.0+✓ renders

Represents a selection of text.

How it works

TextSelection represents the user's current selection within an editable text view — which characters are highlighted, or where the insertion point sits when nothing is highlighted. By exposing the selection as an Equatable value you can observe and store, SwiftUI lets your view react to what the user has chosen without dropping down to platform-specific text engines. Reach for it whenever you need to know whether text is selected, drive formatting controls from the selection, or coordinate UI around the user's focus inside a TextField or TextEditor.

  1. Hold the selection in observable state

    Because the selection changes as the user drags, taps, or moves the cursor, you keep it in a source of truth that SwiftUI can watch. Declare an optional @State private var selection: TextSelection? = nil; an optional value lets the view distinguish "there is a selection" from the initial state where one hasn't been established yet.

  2. Bind it through the selection: parameter

    Editable text views accept a binding to a TextSelection? so they can report and update the selection in step with their content. Pass selection: $selection to TextField("Note", text: $text, selection: $selection) — the same field already bound to $text now also writes the live selection back into your state.

  3. Read the selection to drive the UI

    Once the selection flows into state, treat it like any other value your view depends on. Here the body branches on if selection != nil to show Text("Text is selected") versus Text("No selection"), so the interface stays in sync with what the user has highlighted in the field.

  4. Distinguish a caret from a range

    TextSelection is non-nil both when a span of characters is highlighted and when there's only a blinking insertion point, so selection != nil tells you the field is being edited or focused rather than that a range is necessarily selected. Inspect the value's bounds when you need to tell an empty caret apart from a highlighted range.

Try it — Change the if selection != nil branch's Text("Text is selected") to interpolate the bound text, e.g. Text("Selecting in: \(text)"), then long-press to select and watch the label update as the selection changes.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

TextSelection.swift
struct TextSelectionDemo: View {
    @State private var selection: TextSelection? = nil
    @State private var text = "Select me with a long press"

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            TextField("Note", text: $text, selection: $selection)
                .textFieldStyle(.roundedBorder)
            if selection != nil {
                Text("Text is selected")
                    .foregroundStyle(.secondary)
            } else {
                Text("No selection")
                    .foregroundStyle(.secondary)
            }
        }
        .padding()
    }
}
Live preview
Select me with a long press Text is selected
Select me with a long press Text is selected
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →