How it works
A FocusedBinding is a property wrapper that reads a Binding published by whichever view currently holds focus, letting a distant view read and write that view's state without a direct reference to it. Use it when a command — a toolbar button, a menu item, a keyboard shortcut — needs to act on the data of the focused control, which may change as the user moves around the interface. Where FocusedValue gives you a read-only snapshot of focus-scoped data, FocusedBinding projects a two-way connection, so the consuming view can mutate the focused view's value. Reach for it to wire up editing commands that should always operate on "the thing the user is currently editing."
Define a focused-value key whose Value is a Binding
FocusedBindingresolves its value through a key conforming toFocusedValueKey. For a writable connection the key'sValueassociated type must be aBinding, as instruct NoteKey: FocusedValueKey { typealias Value = Binding<String> }. The wrapper unwraps that binding so the consumer works with the underlying element type directly.Expose the key on FocusedValues
Add a computed property to
FocusedValuesso the key can be referenced as a key path. Hereextension FocusedValuesdeclaresvar noteKey: Binding<String>?with a getter and setter that subscript into the environment viaself[NoteKey.self]. The optional type reflects that no focused view may be publishing a value at a given moment.Publish the binding from the focused view
The view that owns the data advertises its
Bindingfor the current focus scope using thefocusedValue(_:_:)modifier. In the example theTextFieldcalls.focusedValue(\.noteKey, $note), projecting$noteunder thenoteKeykey path so any focus-aware consumer can find it while the field is focused.Read the binding with @FocusedBinding
A separate view declares
@FocusedBinding(\.noteKey) var note: String?to receive the published binding by key path. The wrapper hands back an optional of the bound element —String?rather thanBinding<String>— and assigning through it, as innote = "", writes straight back to the focused view's underlyingnotestate.Treat nil as "no focus" and gate commands
When no view is publishing the key — nothing is focused — the projected value is
nil. Use that to enable or disable the command: theEditorToolbar'sButton("Clear")applies.disabled(note == nil)so it only acts when a focused field is actually supplying a binding to clear.
Button("Clear") enable, then comment out the .focusedValue(\.noteKey, $note) line on the TextField so the binding is never published and the button stays disabled because note is always nil.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 FocusedBindingDemo: View {
@State private var note = "Hello"
var body: some View {
VStack(spacing: 16) {
TextField("Note", text: $note)
.textFieldStyle(.roundedBorder)
.focusedValue(\.noteKey, $note)
EditorToolbar()
Text("You typed: \(note)")
}
.padding()
}
}
struct EditorToolbar: View {
@FocusedBinding(\.noteKey) var note: String?
var body: some View {
Button("Clear") { note = "" }
.disabled(note == nil)
}
}
struct NoteKey: FocusedValueKey {
typealias Value = Binding<String>
}
extension FocusedValues {
var noteKey: Binding<String>? {
get { self[NoteKey.self] }
set { self[NoteKey.self] = newValue }
}
}