Direct and reflect focus in SwiftUI

Description: With device input — as with all things in life — where you put focus matters. Discover how you can move focus in your app with SwiftUI, programmatically dismiss the keyboard, and build large navigation targets from small views. Together, these APIs can help you simplify your app’s interface and make it more powerful for people to find what they need.

  • Focus is the system that lets your app take input from keyboards, remotes, game controllers, accessible switch controls, and other sources that -- unlike touch inputs -- are not tied to specific screen coordinates
  • the focus view is often drawn with special embellishments, making it easy for people to predict where their input will be directed

Focus state API

struct LoginForm {
  enum Field: Hashable {
    case username
    case password
  }

  @State private var username = ""
  @State private var password = ""
  @FocusState private var focusedField: Field?

  var body: some View {
    Form {
      TextField("Username", text: $username)
        .focused($focusedField, equals: .username)

      SecureField("Password", text: $password)
        .focused($focusedField, equals: .password)

      Button("Sign In", action: onSignInTap)
    }
  }

  /// Puts the focus on one of the text fields if their data is missing.
  func onSignInTap() {
    if username.isEmpty {
      focusedField = .username
    } else if password.isEmpty {
      focusedField = .password
    } else {
      handleLogin(username, password)
    }
  }
}
  • Note that the FocusState value is optional (Field? in the example): nil is used for cases where focus is in an unrelated part of the screen.

Focus sections

  • FocusStates are not just for text fields, but can also be used to navigate the focus of the app (think browsing tvOS)
  • new focusSection() view modifier, which tells SwiftUi that this view is capable of accepting focus if it contains any focusable subviews
  • this is used (in tvOS) to navigate between different/separate focus sections of the screen
struct ContentView: View {

  var body: some View {
    HStack {
      VStack) {
         ...
      }
      .focusSection()

      VStack {
        ...
      }
      .focusSection()
    }
  }
}

Missing anything? Corrections? Contributions are welcome 😃

Related

Written by

Federico Zanetello

Federico Zanetello

iOS Engineer with strong passion for Swift, minimalism, and design. When he’s not busy automating things, he can be found writing at FIVE STARS and/or playing with the latest shiny toys.