How do I pass a binding to a focus state?

In a SwiftUI lab, I was asking about setting the focus state down a view hierarchy. The answer I got was to pass the focus state down the views as a binding. Conceptually, that made sense, so I moved on to other questions. But now that I am trying to implement it, I am having problems.

In the parent view, I have something like this:

@FocusState private var focusElement: UUID?

Then I am setting a property like this in the child view:

@Binding var focusedId: UUID?

When I try to create the detail view, I'm trying this:

DetailView(focusedId: $focusElement)

But this doesn't work. The error I get is:

Cannot convert value of type 'FocusState<UUID?>.Binding' to expected argument type 'Binding<UUID?>'

What is the right way to pass down the focus state to a child view so that it can update back up to the parent view?

I am trying to update from one child view, and have a TextField in a sibling view get focus.

Replies

I was able to get this to work on an iOS15 simulator with the following code structure:

struct Parent: View {
    @FocusState var focusedField: UUID?
     
    var body: some View {
        VStack {
            Child(focusedField: $focusedField)
            Sibling(focusedField: $focusedField)
        }
    }
}
struct Child: View {
    var focusedField: FocusState<UUID?>.Binding
    @State var someText: String = ""
    @State var someTextFieldUUID: UUID = UUID()    

    var body: some View {
        VStack {
            TextField("Focusable field", text: $someText)
                 .focused(focusedField, equals: someTextFieldUUID)
        }
    }
}

I checked to make sure I could update focus to parent and sibling fields programmatically.

Note: I think the new focus API doesn't yet work on Xcode previews.

  • An equivalent way is to access the synthesized property. See my answer for details.

Add a Comment

Another way to do this:

struct Parent: View {
    @FocusState var focusedField: UUID?
     
    var body: some View {
        VStack {
            Child(focusedField: _focusedField)
        }
    }
}
struct Child: View {
    @FocusState var focusedField: UUID?
    @State var someText: String = ""
    @State var someTextFieldUUID: UUID = UUID()    

    var body: some View {
        VStack {
            TextField("Focusable field", text: $someText)
                 .focused($focusedField, equals: someTextFieldUUID)
        }
    }
}