TechnologiesSwiftUI

TextSelectionAffinity enum

iOSmacOStvOSwatchOSvisionOSiOS 18.0+✓ renders

A representation of the direction or association of a selection or cursor

How it works

TextSelectionAffinity describes which side of a boundary a text cursor leans toward when the same screen position could belong to two places at once — most notably the wrapping point at the end of a soft-wrapped line, which is simultaneously the end of one visual line and the start of the next. SwiftUI uses the affinity to decide where to draw the insertion point and how it moves, so the cursor lands where the reader expects after wrapping, bidirectional runs, or programmatic placement. Reach for it when you construct or inspect a TextSelection yourself and need that ambiguity resolved deliberately rather than left to the default.

  1. Pass an affinity into TextSelection

    TextSelection models the current selection or caret in an editable text view, and its initializer takes an affinity: alongside the position. In the example the caret is built with TextSelection(insertionPoint:affinity:), pairing the string's startIndex with an explicit affinity so the selection has a well-defined leaning from the moment it's created.

  2. Choose .downstream or .upstream

    TextSelectionAffinity is an enum with two cases. .downstream ties the cursor to the content that follows the boundary (the start of the next line at a wrap), while .upstream ties it to the content before (the end of the preceding line). The example uses affinity: .downstream, so at an ambiguous wrap point the caret prefers the beginning of the following line.

  3. Bind the selection so SwiftUI applies it

    The affinity only takes effect once the selection drives a real editor. Here selection is held in @State and handed to TextField("Type here", text: $text, selection: $selection) via the selection: binding, letting SwiftUI render the insertion point with the chosen affinity and write any user-driven changes back.

  4. Read the affinity back from a selection

    Because affinity is stored on the selection value, you can also inspect it. When the user moves the caret across a wrap, the affinity carried by $selection reflects which line edge the cursor currently favors — useful for status displays or logic that branches on cursor placement, like the Text("Cursor affinity: downstream") caption shown here.

Try it — Change affinity: .downstream to affinity: .upstream in the TextSelection(insertionPoint:affinity:) initializer and watch where the caret settles at a line-wrap boundary.

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.

TextSelectionAffinity.swift
struct TextSelectionAffinityDemo: View {
    @State private var text = "Hello, SwiftUI!"
    @State private var selection = TextSelection(
        insertionPoint: "Hello, SwiftUI!".startIndex,
        affinity: .downstream
    )

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Edit the field below")
                .font(.headline)
            TextField("Type here", text: $text, selection: $selection)
                .textFieldStyle(.roundedBorder)
            Text("Cursor affinity: downstream")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
Edit the field below Hello, SwiftUI! Cursor affinity: downstream
Edit the field below Hello, SwiftUI! Cursor affinity: downstream
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →