Autofocus Textfield Programmatically in Swiftui

Autofocus TextField programmatically in SwiftUI


iOS 15

There is a new wrapper called @FocusState that controls the state of the keyboard and the focused keyboard ('aka' firstResponder).

Become First Responder ( Focused )

If you use a focused modifier on the text fields, you can make them become focused, for example, you can set the focusedField property in the code to make the binded textField become active:

Sample Image

Resign first responder ( Dismiss keyboard )

or dismiss the keyboard by setting the variable to nil:

Sample Image

Don't forget to watch the Direct and reflect focus in SwiftUI session from WWDC2021



iOS 13 and 14 (and 15)

Old but working:

Simple wrapper struct - Works like a native:

Note that Text binding support added as requested in the comments

struct LegacyTextField: UIViewRepresentable {
@Binding public var isFirstResponder: Bool
@Binding public var text: String

public var configuration = { (view: UITextField) in }

public init(text: Binding<String>, isFirstResponder: Binding<Bool>, configuration: @escaping (UITextField) -> () = { _ in }) {
self.configuration = configuration
self._text = text
self._isFirstResponder = isFirstResponder
}

public func makeUIView(context: Context) -> UITextField {
let view = UITextField()
view.addTarget(context.coordinator, action: #selector(Coordinator.textViewDidChange), for: .editingChanged)
view.delegate = context.coordinator
return view
}

public func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text
switch isFirstResponder {
case true: uiView.becomeFirstResponder()
case false: uiView.resignFirstResponder()
}
}

public func makeCoordinator() -> Coordinator {
Coordinator($text, isFirstResponder: $isFirstResponder)
}

public class Coordinator: NSObject, UITextFieldDelegate {
var text: Binding<String>
var isFirstResponder: Binding<Bool>

init(_ text: Binding<String>, isFirstResponder: Binding<Bool>) {
self.text = text
self.isFirstResponder = isFirstResponder
}

@objc public func textViewDidChange(_ textField: UITextField) {
self.text.wrappedValue = textField.text ?? ""
}

public func textFieldDidBeginEditing(_ textField: UITextField) {
self.isFirstResponder.wrappedValue = true
}

public func textFieldDidEndEditing(_ textField: UITextField) {
self.isFirstResponder.wrappedValue = false
}
}
}

Usage:

struct ContentView: View {
@State var text = ""
@State var isFirstResponder = false

var body: some View {
LegacyTextField(text: $text, isFirstResponder: $isFirstResponder)
}
}

Bonus: Completely customizable

LegacyTextField(text: $text, isFirstResponder: $isFirstResponder) {
$0.textColor = .red
$0.tintColor = .blue
}

SwiftUI: How to make TextField become first responder?


iOS 15.0+

macOS 12.0+,

Mac Catalyst 15.0+,

tvOS 15.0+,

watchOS 8.0+

Use focused(_:) if you have a single TextField.

focused(_:)

Modifies this view by binding its focus state to the given Boolean state value.

struct NameForm: View {

@FocusState private var isFocused: Bool

@State private var name = ""

var body: some View {
TextField("Name", text: $name)
.focused($isFocused)

Button("Submit") {
if name.isEmpty {
isFocued = true
}
}
}
}

Use focused(_:equals:) should you have multiple TextFields.

focused(_:equals:)

Modifies this view by binding its focus state to the given state value.

struct LoginForm: View {
enum Field: Hashable {
case usernameField
case passwordField
}

@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: .usernameField)

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

Button("Sign In") {
if username.isEmpty {
focusedField = .usernameField
} else if password.isEmpty {
focusedField = .passwordField
} else {
handleLogin(username, password)
}
}
}
}
}

SwiftUI Documentation

  • focused(_:)
  • focused(_:equals:)
  • @FocusState


Update

I tested this in xCode version 13.0 beta 5 (13A5212g). It works

Disable autofocus on TextField in SwiftUI after App launch

This really looks like a bug (file a feedback to Apple if you want).

A found workaround is to disable temporary control on launch (disabled TextField cannot get focus) and reenable it in next event cycle.

Tested with Xcode 13.3 / macOS 12.2

Here is main part:

TextField("", text: $txt)
.disabled(disabled) // << here !!
.onAppear {
DispatchQueue.main.async { disabled = false } // << here !!
}

Complete code in project is here

Focus on the next TextField/SecureField in SwiftUI

When using UIKit, one would accomplish this by setting up the responder chain. This isn't available in SwiftUI, so until there is a more sophisticated focus and responder system, you can make use of the onEditingChanged changed of TextField

You will then need to manage the state of each field based on stored State variables. It may end up being more work than you want to do.

Fortunately, you can fall back to UIKit in SwiftUI by using UIViewRepresentable.

Here is some code that manages the focus of text fields using the UIKit responder system:

import SwiftUI

struct KeyboardTypeView: View {
@State var firstName = ""
@State var lastName = ""
@State var focused: [Bool] = [true, false]

var body: some View {
Form {
Section(header: Text("Your Info")) {
TextFieldTyped(keyboardType: .default, returnVal: .next, tag: 0, text: self.$firstName, isfocusAble: self.$focused)
TextFieldTyped(keyboardType: .default, returnVal: .done, tag: 1, text: self.$lastName, isfocusAble: self.$focused)
Text("Full Name :" + self.firstName + " " + self.lastName)
}
}
}
}



struct TextFieldTyped: UIViewRepresentable {
let keyboardType: UIKeyboardType
let returnVal: UIReturnKeyType
let tag: Int
@Binding var text: String
@Binding var isfocusAble: [Bool]

func makeUIView(context: Context) -> UITextField {
let textField = UITextField(frame: .zero)
textField.keyboardType = self.keyboardType
textField.returnKeyType = self.returnVal
textField.tag = self.tag
textField.delegate = context.coordinator
textField.autocorrectionType = .no

return textField
}

func updateUIView(_ uiView: UITextField, context: Context) {
if isfocusAble[tag] {
uiView.becomeFirstResponder()
} else {
uiView.resignFirstResponder()
}
}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

class Coordinator: NSObject, UITextFieldDelegate {
var parent: TextFieldTyped

init(_ textField: TextFieldTyped) {
self.parent = textField
}

func updatefocus(textfield: UITextField) {
textfield.becomeFirstResponder()
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

if parent.tag == 0 {
parent.isfocusAble = [false, true]
parent.text = textField.text ?? ""
} else if parent.tag == 1 {
parent.isfocusAble = [false, false]
parent.text = textField.text ?? ""
}
return true
}

}
}

You can refer to this question to get more information about this particular approach.

Hope this helps!

Name-Field in SwiftUI

It sounds like you might be referring to one of the textContentType options.

For example:

struct ContentView: View {
@State private var textFieldContent = ""

var body: some View {
TextField("User name", text: $textFieldContent)
.textContentType(.givenName)
}
}

In this example, the system will suggest a given name for the field based on the contact card set to be the device's owner. On iOS, you can see a list of the UITextContentType options here. On macOS, there's a similar list here, albeit with many fewer options.

focusing a text field in swift

This works for me:

import UIKit

class ViewController:UIViewController, UITextFieldDelegate {

@IBOutlet weak var fNameField: UITextField!
@IBOutlet weak var sNameField: UITextField!
@IBOutlet weak var emailField: UITextField!
@IBOutlet weak var passwordField: UITextField!

override func viewDidLoad() {
super.viewDidLoad()

fNameField.delegate = self
sNameField.delegate = self
emailField.delegate = self
passwordField.delegate = self
}

func isValidEmail (test:String) ->Bool{
// your email validation here...
return true
}

func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
if (textField == self.fNameField) {
self.sNameField.becomeFirstResponder()
}
else if (textField == self.sNameField) {
self.emailField.becomeFirstResponder()

} else if (textField == self.emailField) {
self.passwordField.becomeFirstResponder()
}
else{
var thereWereErrors = checkForErrors()
if !thereWereErrors
{
//conditionally segue to next screen
}
}

return true
}

func checkForErrors() -> Bool
{
var errors = false
let title = "Error"
var message = ""
if fNameField.text.isEmpty {
errors = true
message += "First name empty"
alertWithTitle(title, message: message, ViewController: self, toFocus:self.fNameField)

}
else if sNameField.text.isEmpty
{
errors = true
message += "Surname empty"
alertWithTitle(title, message: message, ViewController: self, toFocus:self.sNameField)

self.sNameField.becomeFirstResponder()
}
else if emailField.text.isEmpty
{
errors = true
message += "Email empty"
alertWithTitle(title, message: message, ViewController: self, toFocus:self.emailField)

}
else if !isValidEmail(emailField.text)
{
errors = true
message += "Invalid Email Address"
alertWithTitle(title, message: message, ViewController: self, toFocus:self.emailField)

}
else if passwordField.text.isEmpty
{
errors = true
message += "Password empty"
alertWithTitle(title, message: message, ViewController: self, toFocus:passwordField)
}
else if count(passwordField.text.utf16)<8
{
errors = true
message += "Password must be at least 8 characters"
alertWithTitle(title, message: message, ViewController: self, toFocus:self.passwordField)
}

return errors
}

func alertWithTitle(title: String!, message: String, ViewController: UIViewController, toFocus:UITextField) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel,handler: {_ in
toFocus.becomeFirstResponder()
});
alert.addAction(action)
ViewController.presentViewController(alert, animated: true, completion:nil)
}

}

MacOS swiftUI Custom ui TextField field with rounded edge

Try to use rounded rectangle in background (with needed parameters, like corner radius, color, etc) instead of border, like

.background(
RoundedRectangle(cornerRadius: 8)
.stroke(your_color_here)
)

How to move to next TextField in SwiftUI?


iOS 15+

Use @FocusState

Before iOS 15

I've taken @Philip Borbon answer and cleaned it up a little bit. I've removed a lot of the customization and kept in the bare minimum to make it easier to see what's required.

struct CustomTextfield: UIViewRepresentable {
let label: String
@Binding var text: String

var focusable: Binding<[Bool]>? = nil

var returnKeyType: UIReturnKeyType = .default

var tag: Int? = nil

var onCommit: (() -> Void)? = nil

func makeUIView(context: Context) -> UITextField {
let textField = UITextField(frame: .zero)
textField.placeholder = label
textField.delegate = context.coordinator

textField.returnKeyType = returnKeyType

if let tag = tag {
textField.tag = tag
}

return textField
}

func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text

if let focusable = focusable?.wrappedValue {
var resignResponder = true

for (index, focused) in focusable.enumerated() {
if uiView.tag == index && focused {
uiView.becomeFirstResponder()
resignResponder = false
break
}
}

if resignResponder {
uiView.resignFirstResponder()
}
}
}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

final class Coordinator: NSObject, UITextFieldDelegate {
let parent: CustomTextfield

init(_ parent: CustomTextfield) {
self.parent = parent
}

func textFieldDidBeginEditing(_ textField: UITextField) {
guard var focusable = parent.focusable?.wrappedValue else { return }

for i in 0...(focusable.count - 1) {
focusable[i] = (textField.tag == i)
}
parent.focusable?.wrappedValue = focusable
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard var focusable = parent.focusable?.wrappedValue else {
textField.resignFirstResponder()
return true
}

for i in 0...(focusable.count - 1) {
focusable[i] = (textField.tag + 1 == i)
}

parent.focusable?.wrappedValue = focusable

if textField.tag == focusable.count - 1 {
textField.resignFirstResponder()
}

return true
}

@objc func textFieldDidChange(_ textField: UITextField) {
parent.text = textField.text ?? ""
}
}
}


Related Topics



Leave a reply



Submit