I'm using SwfitUI with macOS. I have two text fields in a sheet, a Picker and a TextField. I want the second TextField to have focus when a boolean is set if the Picker only has a single element (because there's nothing to choose). Below, please find simplified code where I'm always setting focusRoadNameField
to true
. I'm then using this @FocusState
variable with focused()
on the TextField—but the picker is still always being focused when the sheet opens.
import AppKit
import SwiftUI
struct ContentView: View {
@State private var sheetOpen: Bool = false
@State private var roadName: String = ""
@State private var noFocus: String = ""
@FocusState private var focusRoadNameField: Bool
var body: some View {
Button(action: {
sheetOpen = true
roadName = ""
// And yet the field isn't focused
focusRoadNameField = true
}) {
Text("Open Sheet")
}
.sheet(isPresented: $sheetOpen, content: {
VStack {
TextField("No focus", text: $noFocus)
TextField("Road Name", text: $roadName)
.focused($focusRoadNameField)
HStack {
Button("Cancel", action: {
sheetOpen = false
}).keyboardShortcut(.cancelAction)
.padding()
Spacer()
Button("Add Road", action: {
print("=> Add road '\(roadName)'")
sheetOpen = false
}).keyboardShortcut(.defaultAction)
.padding()
}
}
.frame(width: 260)
.padding()
})
.frame(width: 100, height: 50)
}
}
#Preview {
ContentView()
}
Update: Thanks to Sweeper for the comment. I forgot that I am using Full Keyboard Access, so widgets like the Picker get keyboard focus with a focus ring. I've adjusted the example to use two text fields to show the buggy behavior even on systems which don't have Full Keyboard Access enabled, which is the default state.
Now, you will see the sheet always open with the first, "No focus" field selected, despite the second field having the .focused()
modifier.
I'm using SwfitUI with macOS. I have two text fields in a sheet, a Picker and a TextField. I want the second TextField to have focus when a boolean is set if the Picker only has a single element (because there's nothing to choose). Below, please find simplified code where I'm always setting focusRoadNameField
to true
. I'm then using this @FocusState
variable with focused()
on the TextField—but the picker is still always being focused when the sheet opens.
import AppKit
import SwiftUI
struct ContentView: View {
@State private var sheetOpen: Bool = false
@State private var roadName: String = ""
@State private var noFocus: String = ""
@FocusState private var focusRoadNameField: Bool
var body: some View {
Button(action: {
sheetOpen = true
roadName = ""
// And yet the field isn't focused
focusRoadNameField = true
}) {
Text("Open Sheet")
}
.sheet(isPresented: $sheetOpen, content: {
VStack {
TextField("No focus", text: $noFocus)
TextField("Road Name", text: $roadName)
.focused($focusRoadNameField)
HStack {
Button("Cancel", action: {
sheetOpen = false
}).keyboardShortcut(.cancelAction)
.padding()
Spacer()
Button("Add Road", action: {
print("=> Add road '\(roadName)'")
sheetOpen = false
}).keyboardShortcut(.defaultAction)
.padding()
}
}
.frame(width: 260)
.padding()
})
.frame(width: 100, height: 50)
}
}
#Preview {
ContentView()
}
Update: Thanks to Sweeper for the comment. I forgot that I am using Full Keyboard Access, so widgets like the Picker get keyboard focus with a focus ring. I've adjusted the example to use two text fields to show the buggy behavior even on systems which don't have Full Keyboard Access enabled, which is the default state.
Now, you will see the sheet always open with the first, "No focus" field selected, despite the second field having the .focused()
modifier.
- 1 I cannot reproduce on macOS 15.1.1. The text field is focused as expected. – Sweeper Commented Feb 6 at 15:05
- Thanks for your comment. I have adjusted the MRE to better illustrate the issue. I'm on macOS 15.2 with Xcode 16.2. – Nick K9 Commented Feb 7 at 12:24
1 Answer
Reset to default 0Sheets are modal, and I found that passing @FocusState
can be tricky in iOS18+.
You could try this simple approach using two local @FocusState
and
a basic @State private var focus: Field?
to pass the focus
info to the sheet view. Note capturing
the vars for use in the sheet,
and the .onAppear
.
Example code:
enum Field {
case noFocus
case roadName
}
struct ContentView: View {
@State private var sheetOpen: Bool = false
@State private var roadName: String = "some road"
@State private var noFocus: String = "something"
@State private var text: String = "test"
@State private var focus: Field? // <--- to pass to sheet
@FocusState private var focusField: Field? // <--- local to this view
var body: some View {
VStack {
Button("focus on roadName"){
focus = .roadName // <--- here
sheetOpen = true
}
Button("focus on noFocus"){
focus = .noFocus // <--- here
sheetOpen = true
}
.sheet(isPresented: $sheetOpen) { [$roadName, $noFocus, focus] in // <--- here
MidView(sheetOpen: $sheetOpen,
roadName: $roadName,
noFocus: $noFocus,
focus: focus)
}
}
}
}
struct MidView: View {
@Binding var sheetOpen: Bool
@Binding var roadName: String
@Binding var noFocus: String
var focus: Field? // <--- here
@FocusState private var focusField: Field? // <--- local to this view
var body: some View {
VStack {
TextField("No focus", text: $noFocus)
.focused($focusField, equals: .noFocus)
TextField("Road Name", text: $roadName)
.focused($focusField, equals: .roadName)
HStack {
Button("Cancel", action: {
sheetOpen = false
}).keyboardShortcut(.cancelAction)
.padding()
Spacer()
Button("Add Road", action: {
print("----> Add road '\(roadName)'")
sheetOpen = false
}).keyboardShortcut(.defaultAction)
.padding()
}
}
.frame(width: 260)
.padding()
.onAppear {
focusField = focus // <--- here
}
}
}