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.
Pass an affinity into TextSelection
TextSelectionmodels the current selection or caret in an editable text view, and its initializer takes anaffinity:alongside the position. In the example the caret is built withTextSelection(insertionPoint:affinity:), pairing the string'sstartIndexwith an explicit affinity so the selection has a well-defined leaning from the moment it's created.Choose .downstream or .upstream
TextSelectionAffinityis an enum with two cases..downstreamties the cursor to the content that follows the boundary (the start of the next line at a wrap), while.upstreamties it to the content before (the end of the preceding line). The example usesaffinity: .downstream, so at an ambiguous wrap point the caret prefers the beginning of the following line.Bind the selection so SwiftUI applies it
The affinity only takes effect once the selection drives a real editor. Here
selectionis held in@Stateand handed toTextField("Type here", text: $text, selection: $selection)via theselection:binding, letting SwiftUI render the insertion point with the chosen affinity and write any user-driven changes back.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
affinitycarried by$selectionreflects which line edge the cursor currently favors — useful for status displays or logic that branches on cursor placement, like theText("Cursor affinity: downstream")caption shown here.
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.
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()
}
}