-
Direct and reflect focus in SwiftUI
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.
Ressources
Vidéos connexes
WWDC22
WWDC21
-
Rechercher dans cette vidéo…
-
-
3:38 - Slide 13 - Textfield and Securefield
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } } -
3:49 - Slide 14 - Focus State
import SwiftUI import AuthenticationServices struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } } -
4:07 - Slide 15 - Focus Field
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } } -
4:32 - Slide 17 - focused modifiers
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } } -
6:07 - Slide 25 - onSubmit
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" @State private var submittedEmail: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) .onSubmit { submittedEmail = email if !isEmailValid { focusedField = .email } } } } private var isEmailValid : Bool { let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let predicate = NSPredicate(format:"SELF MATCHES %@", regex) return submittedEmail.isEmpty || predicate.evaluate(with: submittedEmail) } } -
6:25 - Slide 26 - border
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" @State private var submittedEmail: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) .border(Color.red, width: (focusedField == .email && !isEmailValid) ? 2 : 0) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) .onSubmit { submittedEmail = email if !isEmailValid { focusedField = .email } } } } private var isEmailValid : Bool { let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let predicate = NSPredicate(format:"SELF MATCHES %@", regex) return submittedEmail.isEmpty || predicate.evaluate(with: submittedEmail) } } -
7:17 - Slide 29 - dismiss keyboard with nil
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" @State private var submittedEmail: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) .border(Color.red, width: (focusedField == .email && !isEmailValid) ? 2 : 0) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) .onSubmit { submittedEmail = email if !isEmailValid { focusedField = .email } else { focusedField = nil // Show progress indicator, and log in. } } } } private var isEmailValid : Bool { let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let predicate = NSPredicate(format:"SELF MATCHES %@", regex) return submittedEmail.isEmpty || predicate.evaluate(with: submittedEmail) } } -
9:24 - tv code
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { HStack { VStack(alignment: .leading) { Spacer(minLength:60).frame(height: 150) Text("Vacation\nPlanner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(Color.black.opacity(0.8)) .lineLimit(nil) .multilineTextAlignment(.center) .padding(.horizontal, 40) Spacer().frame(height:80) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) Spacer().frame(height:30) SecureField("Password", text: $password) .submitLabel(.go) .textContentType(.password) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(Color.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) Spacer() } .frame(width: 350, alignment: .center) VStack { Image(photoName) .resizable() .frame(width: 1400) .aspectRatio(contentMode: .fit) .ignoresSafeArea(edges: [.trailing]) BrowsePhotosButton() } }.preferredColorScheme(.light) } } -
9:47 - focus section 1
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { HStack { VStack(alignment: .leading) { Spacer(minLength:60).frame(height: 150) Text("Vacation\nPlanner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(Color.black.opacity(0.8)) .lineLimit(nil) .multilineTextAlignment(.center) .padding(.horizontal, 40) Spacer().frame(height:80) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) Spacer().frame(height:30) SecureField("Password", text: $password) .submitLabel(.go) .textContentType(.password) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(Color.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) Spacer() } .frame(width: 350, alignment: .center) VStack { Image(photoName) .resizable() .frame(width: 1400) .aspectRatio(contentMode: .fit) .ignoresSafeArea(edges: [.trailing]) BrowsePhotosButton() } .focusSection() }.preferredColorScheme(.light) } } -
10:06 - focus section 2
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { HStack { VStack(alignment: .leading) { Spacer(minLength:60).frame(height: 150) Text("Vacation\nPlanner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(Color.black.opacity(0.8)) .lineLimit(nil) .multilineTextAlignment(.center) .padding(.horizontal, 40) Spacer().frame(height:80) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) Spacer().frame(height:30) SecureField("Password", text: $password) .submitLabel(.go) .textContentType(.password) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(Color.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) Spacer() } .frame(width: 350, alignment: .center) .focusSection() VStack { Image(photoName) .resizable() .frame(width: 1400) .aspectRatio(contentMode: .fit) .ignoresSafeArea(edges: [.trailing]) BrowsePhotosButton() } .focusSection() }.preferredColorScheme(.light) } }
-