TechnologiesSwiftUIPickers and Text Fields

AttributedTextSelection struct

iOSmacOStvOSwatchOSvisionOSiOS 26.0+✓ renders

Represents a selection of attributed text.

How it works

AttributedTextSelection represents the current selection range within editable attributed text, tracking which characters the user has highlighted in a TextEditor or text field bound to an AttributedString. Because attributed text carries styling at the run level, a plain character offset isn't enough to address a selection safely as the text mutates; AttributedTextSelection models the selection in terms that stay valid against the underlying AttributedString. Reach for it when you want to read or act on whatever the user has selected — for example, to apply formatting to just the highlighted portion of rich text.

  1. Hold the selection in @State

    AttributedTextSelection is a value type you own alongside the text itself, so you store it in state and let the editor mutate it. Here @State private var selection = AttributedTextSelection() starts as an empty selection and updates as the user clicks and drags.

  2. Bind it to the editor with selection:

    An editable text view that works on rich text accepts a selection binding next to its text binding, keeping AttributedTextSelection in sync with what's highlighted on screen. The example wires both together as TextEditor(text: $text, selection: $selection), so every drag updates selection live.

  3. Resolve the selected ranges with indices(in:)

    To act on the selection you ask it for concrete positions in a given AttributedString via indices(in:), which returns a value you can pattern-match. The code unwraps it with if case let .ranges(ranges) = selection.indices(in: text), yielding the set of character ranges the user picked.

  4. Mutate styling through transform(updating:)

    Editing the AttributedString can shift indices, so changes flow through transform(updating:), which keeps the selection consistent as runs are rewritten. Inside the closure the example walks ranges.ranges and sets runs[range].font = .body.bold(), bolding exactly the selected characters while AttributedTextSelection is migrated forward.

Try it — In the transform(updating:) closure, change runs[range].font = .body.bold() to runs[range].foregroundColor = .red and watch the same selection drive a color change instead of a weight change.

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.

AttributedTextSelection.swift
struct AttributedTextSelectionDemo: View {
    @State private var text = AttributedString("Edit and select this text")
    @State private var selection = AttributedTextSelection()

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            TextEditor(text: $text, selection: $selection)
                .frame(height: 120)
                .border(.secondary)

            Button("Bold Selection") {
                text.transform(updating: &selection) { runs in
                    if case let .ranges(ranges) = selection.indices(in: text) {
                        for range in ranges.ranges {
                            runs[range].font = .body.bold()
                        }
                    }
                }
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}
Live preview
Edit and select this text Bold Selection
Edit and select this text Bold Selection
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →