VButko

How to manage Focus State using @FocusState in SwiftUI

Since iOS15, we can use @FocusState property wrapper to manage the focus state of the fields, which will also trigger showing and dismissing the keyboard.

In iOS14 and before, we need some more work to do for the same result. You can find out the implementation details in the article: How to manage SwiftUI Focus State in iOS14 and before?

Using @FocusState in iOS15

For a more flexible solution, we can create an enum for fields that we will use for focus state:

enum Field {
    case title
    case note
}

Next, we create a @FocusState property that will hold the value of Field enum that should be in focus:

struct AddNewTaskView: View {
    @FocusState private var focusedField: Field?
        ...
}

Then we should bind a view to a focus state using .focused(_:equals) modifier:

TextField("Title", text: $viewModel.title)
    .focused($focusedField, equals: .title)

Finally, in order to trigger the focused state, we should add a .task modifier that will set the focused property when the view appears:

var body: some View {
        VStack {
                TextField(viewModel.taskType.placeholder, text: $viewModel.title)
                    .focused($focusedField, equals: .title)
        }
        .task {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
            focusedField = .title
        }
    }
}

Here we're using a 0.15 seconds delay, but you should always test multiple times if the value works for you. A more complicated screen and slower device may need a 0.5 seconds delay. For instance, a popup view on iPad required at least 0.65 seconds delay in my case.

Conclusion

Managing focus state is a pretty common task in app development and luckily, we have a flexible built-in solution in iOS15.

I hope you enjoyed this article. If you have any questions, suggestions, or feedback, please let me know on Twitter.

Thanks for reading!