Related
I am trying to check when a text field changes, equivalent too the function used for textView - textViewDidChange so far I have done this:
func textFieldDidBeginEditing(textField: UITextField) {
if self.status.text == "" && self.username.text == "" {
self.topRightButton.enabled = false
} else {
self.topRightButton.enabled = true
}
}
Which kind of works, but the topRightButton is enabled as soon as the text field is pressed on, I want it to be enabled only when text is actually typed in?
SWIFT
Swift 4.2
textfield.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)
and
#objc func textFieldDidChange(_ textField: UITextField) {
}
SWIFT 3 & swift 4.1
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)
and
func textFieldDidChange(_ textField: UITextField) {
}
SWIFT 2.2
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)
and
func textFieldDidChange(textField: UITextField) {
//your code
}
OBJECTIVE-C
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
and textFieldDidChange method is
-(void)textFieldDidChange :(UITextField *) textField{
//your code
}
You can make this connection in interface builder.
In your storyboard, click the assistant editor at the top of the screen (two circles in the middle).
Ctrl + Click on the textfield in interface builder.
Drag from EditingChanged to inside your view controller class in the assistant view.
Name your function ("textDidChange" for example) and click connect.
Swift 5.0
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
for: .editingChanged)
and handle method:
#objc func textFieldDidChange(_ textField: UITextField) {
}
Swift 4.0
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
and handle method:
#objc func textFieldDidChange(_ textField: UITextField) {
}
Swift 3.0
textField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
and handle method:
func textFieldDidChange(textField: UITextField) {
}
The way I've handled it so far: in UITextFieldDelegate
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
// text hasn't changed yet, you have to compute the text AFTER the edit yourself
let updatedString = (textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)
// do whatever you need with this updated string (your code)
// always return true so that changes propagate
return true
}
Swift4 version
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
return true
}
Swift 3
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(sender:)), for: UIControlEvents.editingChanged)
textField(_:shouldChangeCharactersIn:replacementString:) worked for me in Xcode 8, Swift 3 if you want to check every single keypress.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Whatever code you want to run here.
// Keep in mind that the textfield hasn't yet been updated,
// so use 'string' instead of 'textField.text' if you want to
// access the string the textfield will have after a user presses a key
var statusText = self.status.text
var usernameText = self.username.text
switch textField{
case self.status:
statusText = string
case self.username:
usernameText = string
default:
break
}
if statusText == "" && usernameText == "" {
self.topRightButton.enabled = false
} else {
self.topRightButton.enabled = true
}
//Return false if you don't want the textfield to be updated
return true
}
Swift 3.0.1+ (Some of the other swift 3.0 answers are not up to date)
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
func textFieldDidChange(_ textField: UITextField) {
}
You can use this delegate method from UITextFieldDelegate. It fires with every character change.
(Objective C) textField:shouldChangeCharactersInRange:replacementString:
(Swift) textField(_:shouldChangeCharactersInRange:replacementString:)
However THIS ONLY FIRES BEFORE a change is made (indeed, a change is only made if you do return true from here).
Swift 4
Conform to UITextFieldDelegate.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// figure out what the new string will be after the pending edit
let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
// Do whatever you want here
// Return true so that the change happens
return true
}
Maybe use RxSwift ?
need
pod 'RxSwift', '~> 3.0'
pod 'RxCocoa', '~> 3.0'
add imports obviously
import RxSwift
import RxCocoa
So u have a textfield : UITextField
let observable: Observable<String?> = textField.rx.text.asObservable()
observable.subscribe(
onNext: {(string: String?) in
print(string!)
})
U have other 3 methods..
onError
onCompleted
onDisposed
onNext
Swift 4
textField.addTarget(self, action: #selector(textIsChanging), for: UIControlEvents.editingChanged)
#objc func textIsChanging(_ textField:UITextField) {
print ("TextField is changing")
}
If you want to make a change once the user has typed in completely (It will be called once user dismiss keyboard or press enter).
textField.addTarget(self, action: #selector(textDidChange), for: UIControlEvents.editingDidEnd)
#objc func textDidChange(_ textField:UITextField) {
print ("TextField did changed")
}
You should follow this steps:
Make a Outlet reference to the textfield
AssignUITextFieldDelegate to the controller class
Configure yourTextField.delegate
Implement whatever function you need
Sample code:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var yourTextFiled : UITextField!
override func viewDidLoad() {
super.viewDidLoad()
yourTextFiled.delegate = self
}
func textFieldDidEndEditing(_ textField: UITextField) {
// your code
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// your code
}
.
.
.
}
In case it is not possible to bind the addTarget to your UITextField, I advise you to bind one of them as suggested above, and insert the code for execution at the end of the shouldChangeCharactersIn method.
nameTextField.addTarget(self, action: #selector(RegistrationViewController.textFieldDidChange(_:)), for: .editingChanged)
#objc func textFieldDidChange(_ textField: UITextField) {
if phoneNumberTextField.text!.count == 17 && nameTextField.text!.count > 0 {
continueButtonOutlet.backgroundColor = UIColor(.green)
} else {
continueButtonOutlet.backgroundColor = .systemGray
}
}
And in call in shouldChangeCharactersIn func.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else {
return true
}
let lastText = (text as NSString).replacingCharacters(in: range, with: string) as String
if phoneNumberTextField == textField {
textField.text = lastText.format("+7(NNN)-NNN-NN-NN", oldString: text)
textFieldDidChange(phoneNumberTextField)
return false
}
return true
}
There's now a UITextField delegate method available on iOS13+
optional func textFieldDidChangeSelection(_ textField: UITextField)
txf_Subject.addTarget(self, action:#selector(didChangeFirstText), for: .editingChanged)
#objc func didChangeText(textField:UITextField) {
let str = textField.text
if(str?.contains(" "))!{
let newstr = str?.replacingOccurrences(of: " ", with: "")
textField.text = newstr
}
}
#objc func didChangeFirstText(textField:UITextField) {
if(textField.text == " "){
textField.text = ""
}
}
Swift 4.2
write this in viewDidLoad
// to detect if TextField changed
TextField.addTarget(self, action: #selector(textFieldDidChange(_:)),
for: UIControl.Event.editingChanged)
write this outside viewDidLoad
#objc func textFieldDidChange(_ textField: UITextField) {
// do something
}
You could change the event by UIControl.Event.editingDidBegin or what ever you want to detect.
This is how you can add a textField text change listener using Swift 3:
Declare your class as UITextFieldDelegate
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textField.addTarget(self, action: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:)), for: UIControlEvents.editingChanged)
}
Then just traditionally add a textFieldShouldEndEditing function:
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { // do stuff
return true
}
Just in case you are interested in a SwiftUI solution, this it's working for me:
TextField("write your answer here...",
text: Binding(
get: {
return self.query
},
set: { (newValue) in
self.fetch(query: newValue) // any action you need
return self.query = newValue
}
)
)
I have to say it's not my idea, I read it in this blog: SwiftUI binding: A very simple trick
You can manage editing either by "shouldchangecharacter" delgate or "begin and end" textfield delegate. Don't forget to set delegate
I am manage to do this using "did begin and end editing" below.
//MARK:- TextViewDelegate
extension ViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
let count = self.tfEmail.text?.count ?? 0
if textField == self.tfEmail {
if count == 0{
//Empty textfield
}else{
//Non-Empty textfield
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.tfEmail{
//when user taps on textfield
}
}
}
swift 4
In viewDidLoad():
//ADD BUTTON TO DISMISS KEYBOARD
// Init a keyboard toolbar
let toolbar = UIView(frame: CGRect(x: 0, y: view.frame.size.height+44, width: view.frame.size.width, height: 44))
toolbar.backgroundColor = UIColor.clear
// Add done button
let doneButt = UIButton(frame: CGRect(x: toolbar.frame.size.width - 60, y: 0, width: 44, height: 44))
doneButt.setTitle("Done", for: .normal)
doneButt.setTitleColor(MAIN_COLOR, for: .normal)
doneButt.titleLabel?.font = UIFont(name: "Titillium-Semibold", size: 13)
doneButt.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
toolbar.addSubview(doneButt)
USDTextField.inputAccessoryView = toolbar
Add this function:
#objc func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
I have 4 UITextFields in one UIView like this:
each UITextField limited to 4 characters. i want implement a auto switching between UITextFields with count characters of each UITextField.
i use shouldChangeCharactersIn for limitation characters:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let maxLength = 4
var newString = String()
let currentString: NSString = textField.text! as NSString
newString = currentString.replacingCharacters(in: range, with: string)
return newString.length <= maxLength
}
and this is my switching implement:
func textFieldDidChange(_ textField: UITextField){
let text = textField.text
if text?.utf16.count == 4 {
switch textField {
case firstPartTextField:
secondPartTextField.becomeFirstResponder()
case secondPartTextField:
thirdPartTextField.becomeFirstResponder()
case thirdPartTextField:
fourthPartTextField.becomeFirstResponder()
default:
break
}
}
}
my problem is switch to previous UITextField in deleting texts, i can not detect backSpace event when UITextField is empty.
Add target for textField in viewDidLoad()
firstTxt.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
secTxt.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
thirdTxt.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
fourTXt.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
Than create selector method
func textFieldDidChange(textField: UITextField){
let text = textField.text
if text?.utf16.count == 1{
switch textField{
case firstTxt:
secTxt.becomeFirstResponder()
case secTxt:
thirdTxt.becomeFirstResponder()
case thirdTxt:
fourTXt.becomeFirstResponder()
case fourTXt:
fourTXt.resignFirstResponder()
default:
break
}
}else{
}
}
Make an outlet collection of all your textfields and configure them like so:
OutletCollection:
#IBOutlet var textfields: [UITextField]!
Add this in viewDidLoad:
for (i, tf) in textfields.enumerated() {
tf.layer.cornerRadius = 5
tf.clipsToBounds = true
tf.tag = i
tf.delegate = self
}
In the target function, you'll restrict the count in this manner:
func textFieldDidChange(textField: UITextField) {
let text = textField.text!
if text.utf16.count >= 4 {
if textField.tag < 4 {
codeTextfields[textField.tag + 1].becomeFirstResponder()
}
else {
textField.resignFirstResponder()
}
}
}
This will the change in one of your delegate methods:
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
Refined version of the following answer: How to move cursor from one text field to another automatically in swift ios programmatically?
func textFieldDidBeginEditing(textField: UITextField) {
scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70), animated: true)
if(textField == firstDigit){
textField.becomeFirstResponder()
secondDigit.resignFirstResponder()
}
else if(textField == secondDigit){
textField.becomeFirstResponder()
thirdDigit.resignFirstResponder()
}
else if(textField == thirdDigit){
//textField.becomeFirstResponder()
fourthDigit.becomeFirstResponder()
}
I am using four textfields for OTP entry in which only one number can be entered at a time. After entering the number I need to move the cursor automatically to next textfield.
Set textField delegate and add target:
override func viewDidLoad() {
super.viewDidLoad()
first.delegate = self
second.delegate = self
third.delegate = self
fourth.delegate = self
first.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
second.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
third.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
fourth.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
Now when text changes change textField
func textFieldDidChange(textField: UITextField){
let text = textField.text
if text?.utf16.count >= 1{
switch textField{
case first:
second.becomeFirstResponder()
case second:
third.becomeFirstResponder()
case third:
fourth.becomeFirstResponder()
case fourth:
fourth.resignFirstResponder()
default:
break
}
}else{
}
}
And lastly when user start editing clear textField
extension ViewController: UITextFieldDelegate{
func textFieldDidBeginEditing(textField: UITextField) {
textField.text = ""
}
}
update Solution For Swift 5
In This solution, You will go to next Field. And When You Press Erase will come at previous text field.
Step 1: Set Selector for Text Field
override func viewDidLoad() {
super.viewDidLoad()
otpTextField1.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
otpTextField2.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
otpTextField3.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
otpTextField4.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
}
Step 2: Now We will handle move next text Field and Erase text Field.
#objc func textFieldDidChange(textField: UITextField){
let text = textField.text
if text?.count == 1 {
switch textField{
case otpTextField1:
otpTextField2.becomeFirstResponder()
case otpTextField2:
otpTextField3.becomeFirstResponder()
case otpTextField3:
otpTextField4.becomeFirstResponder()
case otpTextField4:
otpTextField4.resignFirstResponder()
default:
break
}
}
if text?.count == 0 {
switch textField{
case otpTextField1:
otpTextField1.becomeFirstResponder()
case otpTextField2:
otpTextField1.becomeFirstResponder()
case otpTextField3:
otpTextField2.becomeFirstResponder()
case otpTextField4:
otpTextField3.becomeFirstResponder()
default:
break
}
}
else{
}
}
Important Note: Don't Forget To set Delegate.
Swift 3 code to move the cursor from one field to another automatically in OTP(One Time Password) fields.
//Add all outlet in your code.
#IBOutlet weak var otpbox1: UITextField!
#IBOutlet weak var otpbox2: UITextField!
#IBOutlet weak var otpbox3: UITextField!
#IBOutlet weak var otpbox4: UITextField!
#IBOutlet weak var otpbox5: UITextField!
#IBOutlet weak var otpbox6: UITextField!
// Add the delegate in viewDidLoad
func viewDidLoad() {
super.viewDidLoad()
otpbox1?.delegate = self
otpbox2?.delegate = self
otpbox3?.delegate = self
otpbox4?.delegate = self
otpbox5?.delegate = self
otpbox6?.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool {
// Range.length == 1 means,clicking backspace
if (range.length == 0){
if textField == otpbox1 {
otpbox2?.becomeFirstResponder()
}
if textField == otpbox2 {
otpbox3?.becomeFirstResponder()
}
if textField == otpbox3 {
otpbox4?.becomeFirstResponder()
}
if textField == otpbox4 {
otpbox5?.becomeFirstResponder()
}
if textField == otpbox5 {
otpbox6?.becomeFirstResponder()
}
if textField == otpbox6 {
otpbox6?.resignFirstResponder() /*After the otpbox6 is filled we capture the All the OTP textField and do the server call. If you want to capture the otpbox6 use string.*/
let otp = "\((otpbox1?.text)!)\((otpbox2?.text)!)\((otpbox3?.text)!)\((otpbox4?.text)!)\((otpbox5?.text)!)\(string)"
}
textField.text? = string
return false
}else if (range.length == 1) {
if textField == otpbox6 {
otpbox5?.becomeFirstResponder()
}
if textField == otpbox5 {
otpbox4?.becomeFirstResponder()
}
if textField == otpbox4 {
otpbox3?.becomeFirstResponder()
}
if textField == otpbox3 {
otpbox2?.becomeFirstResponder()
}
if textField == otpbox2 {
otpbox1?.becomeFirstResponder()
}
if textField == otpbox1 {
otpbox1?.resignFirstResponder()
}
textField.text? = ""
return false
}
return true
}
This is similar to how UberEats has their otp fields. You can just copy and paste this into a file and run it to see how it works. But don't forget to add the MyTextField class or it won't work.
If you want it to automatically to move to the next textfield after a number is entered and still be able to move backwards if the back button is pressed WHILE the textField is empty this will help you.
Like the very first thing I said, this is similar to how UberEats has their sms textFields working. You can't just randomly press a textField and select it. Using this you can only move forward and backwards. The ux is subjective but if Uber uses it the ux must be valid. I say it's similar because they also have a gray box covering the textField so I'm not sure what's going on behind it. This was the closest I could get.
First your going to have to subclass UITextField using this answer to detect when the backspace button is pressed. When the back button is pressed your going to erase everything inside that field AND the previous field then jump to the previous field.
Second your going to have to prevent the user from being able to select the left side of the cursor once a char is inside the textField using this answer. You override the method in the same subClass from the first step.
Third you need to detect which textField is currently active using this answer
Fourth your going to have to run some checks inside func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool using this YouTube tutorial. I added some things to his work.
I'm doing everything programmatically so you can copy and paste the entire code into a project and run it
First create a subClass of UITextField and name it MyTextField:
protocol MyTextFieldDelegate: class {
func textFieldDidDelete()
}
// 1. subclass UITextField and create protocol for it to know when the backButton is pressed
class MyTextField: UITextField {
weak var myDelegate: MyTextFieldDelegate? // make sure to declare this as weak to prevent a memory leak/retain cycle
override func deleteBackward() {
super.deleteBackward()
myDelegate?.textFieldDidDelete()
}
// when a char is inside the textField this keeps the cursor to the right of it. If the user can get on the left side of the char and press the backspace the current char won't get deleted
override func closestPosition(to point: CGPoint) -> UITextPosition? {
let beginning = self.beginningOfDocument
let end = self.position(from: beginning, offset: self.text?.count ?? 0)
return end
}
}
Second inside the class with the OTP textfields, set the class to use the UITextFieldDelegate and the MyTextFieldDelegate, then create a class property and name it activeTextField. When whichever textField becomes active inside textFieldDidBeginEditing you set the activeTextField to that. In viewDidLoad set all the textFields to use both delegates.
Make sure the First otpTextField is ENABLED and the second, third, and fourth otpTextFields are ALL initially DIASABLED
import UIKit
// 2. set the class to BOTH Delegates
class ViewController: UIViewController, UITextFieldDelegate, MyTextFieldDelegate {
let staticLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 17)
label.text = "Enter the SMS code sent to your phone"
return label
}()
// 3. make each textField of type MYTextField
let otpTextField1: MyTextField = {
let textField = MyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 25)
textField.autocorrectionType = .no
textField.keyboardType = .numberPad
textField.textAlignment = .center
// **important this is initially ENABLED
return textField
}()
let otpTextField2: MyTextField = {
let textField = MyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 25)
textField.autocorrectionType = .no
textField.keyboardType = .numberPad
textField.textAlignment = .center
textField.isEnabled = false // **important this is initially DISABLED
return textField
}()
let otpTextField3: MyTextField = {
let textField = MyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 25)
textField.autocorrectionType = .no
textField.keyboardType = .numberPad
textField.textAlignment = .center
textField.isEnabled = false // **important this is initially DISABLED
return textField
}()
let otpTextField4: MyTextField = {
let textField = MyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 25)
textField.autocorrectionType = .no
textField.keyboardType = .numberPad
textField.textAlignment = .center
textField.isEnabled = false // **important this is initially DISABLED
return textField
}()
// 4. create this property to know which textField is active. Set it in step 8 and use it in step 9
var activeTextField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// 5. set the regular UItextField delegate to each textField
otpTextField1.delegate = self
otpTextField2.delegate = self
otpTextField3.delegate = self
otpTextField4.delegate = self
// 6. set the subClassed textField delegate to each textField
otpTextField1.myDelegate = self
otpTextField2.myDelegate = self
otpTextField3.myDelegate = self
otpTextField4.myDelegate = self
configureAnchors()
// 7. once the screen appears show the keyboard
otpTextField1.becomeFirstResponder()
}
// 8. when a textField is active set the activeTextField property to that textField
func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField
}
// 9. when the backButton is pressed, the MyTextField delegate will get called. The activeTextField will let you know which textField the backButton was pressed in. Depending on the textField certain textFields will become enabled and disabled.
func textFieldDidDelete() {
if activeTextField == otpTextField1 {
print("backButton was pressed in otpTextField1")
// do nothing
}
if activeTextField == otpTextField2 {
print("backButton was pressed in otpTextField2")
otpTextField2.isEnabled = false
otpTextField1.isEnabled = true
otpTextField1.becomeFirstResponder()
otpTextField1.text = ""
}
if activeTextField == otpTextField3 {
print("backButton was pressed in otpTextField3")
otpTextField3.isEnabled = false
otpTextField2.isEnabled = true
otpTextField2.becomeFirstResponder()
otpTextField2.text = ""
}
if activeTextField == otpTextField4 {
print("backButton was pressed in otpTextField4")
otpTextField4.isEnabled = false
otpTextField3.isEnabled = true
otpTextField3.becomeFirstResponder()
otpTextField3.text = ""
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text {
// 10. when the user enters something in the first textField it will automatically adjust to the next textField and in the process do some disabling and enabling. This will proceed until the last textField
if (text.count < 1) && (string.count > 0) {
if textField == otpTextField1 {
otpTextField1.isEnabled = false
otpTextField2.isEnabled = true
otpTextField2.becomeFirstResponder()
}
if textField == otpTextField2 {
otpTextField2.isEnabled = false
otpTextField3.isEnabled = true
otpTextField3.becomeFirstResponder()
}
if textField == otpTextField3 {
otpTextField3.isEnabled = false
otpTextField4.isEnabled = true
otpTextField4.becomeFirstResponder()
}
if textField == otpTextField4 {
// do nothing or better yet do something now that you have all four digits for the sms code. Once the user lands on this textField then the sms code is complete
}
textField.text = string
return false
} // 11. if the user gets to the last textField and presses the back button everything above will get reversed
else if (text.count >= 1) && (string.count == 0) {
if textField == otpTextField2 {
otpTextField2.isEnabled = false
otpTextField1.isEnabled = true
otpTextField1.becomeFirstResponder()
otpTextField1.text = ""
}
if textField == otpTextField3 {
otpTextField3.isEnabled = false
otpTextField2.isEnabled = true
otpTextField2.becomeFirstResponder()
otpTextField2.text = ""
}
if textField == otpTextField4 {
otpTextField4.isEnabled = false
otpTextField3.isEnabled = true
otpTextField3.becomeFirstResponder()
otpTextField3.text = ""
}
if textField == otpTextField1 {
// do nothing
}
textField.text = ""
return false
} // 12. after pressing the backButton and moving forward again you will have to do what's in step 10 all over again
else if text.count >= 1 {
if textField == otpTextField1 {
otpTextField1.isEnabled = false
otpTextField2.isEnabled = true
otpTextField2.becomeFirstResponder()
}
if textField == otpTextField2 {
otpTextField2.isEnabled = false
otpTextField3.isEnabled = true
otpTextField3.becomeFirstResponder()
}
if textField == otpTextField3 {
otpTextField3.isEnabled = false
otpTextField4.isEnabled = true
otpTextField4.becomeFirstResponder()
}
if textField == otpTextField4 {
// do nothing or better yet do something now that you have all four digits for the sms code. Once the user lands on this textField then the sms code is complete
}
textField.text = string
return false
}
}
return true
}
//**Optional** For a quick setup use this below. Here is how to add a gray line to the textFields and here are the anchors:
// if your app supports portrait and horizontal your going to have to make some adjustments to this every time the phone rotates
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
addBottomLayerTo(textField: otpTextField1)
addBottomLayerTo(textField: otpTextField2)
addBottomLayerTo(textField: otpTextField3)
addBottomLayerTo(textField: otpTextField4)
}
// this adds a lightGray line at the bottom of the textField
func addBottomLayerTo(textField: UITextField) {
let layer = CALayer()
layer.backgroundColor = UIColor.lightGray.cgColor
layer.frame = CGRect(x: 0, y: textField.frame.height - 2, width: textField.frame.width, height: 2)
textField.layer.addSublayer(layer)
}
func configureAnchors() {
view.addSubview(staticLabel)
view.addSubview(otpTextField1)
view.addSubview(otpTextField2)
view.addSubview(otpTextField3)
view.addSubview(otpTextField4)
let width = view.frame.width / 5
staticLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15).isActive = true
staticLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
staticLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
// textField 1
otpTextField1.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
otpTextField1.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
otpTextField1.widthAnchor.constraint(equalToConstant: width).isActive = true
otpTextField1.heightAnchor.constraint(equalToConstant: width).isActive = true
// textField 2
otpTextField2.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
otpTextField2.leadingAnchor.constraint(equalTo: otpTextField1.trailingAnchor, constant: 10).isActive = true
otpTextField2.widthAnchor.constraint(equalTo: otpTextField1.widthAnchor).isActive = true
otpTextField2.heightAnchor.constraint(equalToConstant: width).isActive = true
// textField 3
otpTextField3.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
otpTextField3.leadingAnchor.constraint(equalTo: otpTextField2.trailingAnchor, constant: 10).isActive = true
otpTextField3.widthAnchor.constraint(equalTo: otpTextField1.widthAnchor).isActive = true
otpTextField3.heightAnchor.constraint(equalToConstant: width).isActive = true
// textField 4
otpTextField4.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
otpTextField4.leadingAnchor.constraint(equalTo: otpTextField3.trailingAnchor, constant: 10).isActive = true
otpTextField4.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
otpTextField4.widthAnchor.constraint(equalTo: otpTextField1.widthAnchor).isActive = true
otpTextField4.heightAnchor.constraint(equalToConstant: width).isActive = true
}
}
This is separate from the answer above but if you need to add multiple characters to each otpTextField then follow this answer.
Firstly we'll need to set the tag for the UITextField;
func textFieldShouldReturnSingle(_ textField: UITextField , newString : String)
{
let nextTag: Int = textField.tag + 1
let nextResponder: UIResponder? = textField.superview?.superview?.viewWithTag(nextTag)
textField.text = newString
if let nextR = nextResponder
{
// Found next responder, so set it.
nextR.becomeFirstResponder()
}
else
{
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = ((textField.text)! as NSString).replacingCharacters(in: range, with: string)
let newLength = newString.characters.count
if newLength == 1 {
textFieldShouldReturnSingle(textField , newString : newString)
return false
}
return true
}
Note: The UITextField takes only one character in number format, which is in OTP format.
Objective c and Swift 4.2 to move the cursor from one field to another automatically in OTP(One Time Password) fields
Here i am taking one view controller
]1
Then give the Tag values for each TextFiled.Those related reference images are shown below
Enter tag value for first textfiled --> 1,2ndTextfiled ---->2,3rd TextFiled --->3 4rth TextFiled---->4
Then assign Textfiled Delegates and write below code and see the magic
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString
*)string
{
if ((textField.text.length < 1) && (string.length > 0))
{
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview
viewWithTag:nextTag];
if (! nextResponder){
[textField resignFirstResponder];
}
textField.text = string;
if (nextResponder)
[nextResponder becomeFirstResponder];
return NO;
}else if ((textField.text.length >= 1) && (string.length == 0)){
// on deleteing value from Textfield
NSInteger prevTag = textField.tag - 1;
// Try to find prev responder
UIResponder* prevResponder = [textField.superview
viewWithTag:prevTag];
if (! prevResponder){
[textField resignFirstResponder];
}
textField.text = string;
if (prevResponder)
// Found next responder, so set it.
[prevResponder becomeFirstResponder];
return NO;
}
return YES;
}
swift4.2 version code
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text!.count < 1 && string.count > 0 {
let tag = textField.tag + 1;
let nextResponder = textField.superview?.viewWithTag(tag)
if (nextResponder != nil){
textField.resignFirstResponder()
}
textField.text = string;
if (nextResponder != nil){
nextResponder?.becomeFirstResponder()
}
return false;
}else if (textField.text?.count)! >= 1 && string.count == 0 {
let prevTag = textField.tag - 1
let prevResponser = textField.superview?.viewWithTag(prevTag)
if (prevResponser != nil){
textField.resignFirstResponder()
}
textField.text = string
if (prevResponser != nil){
prevResponser?.becomeFirstResponder()
}
return false
}
return true;
}
Here I was take 4 TextField
#IBOutlet var txtOtp: [BottomBorderTextField]!
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
defer{
if !string.isEmpty {
textField.text = string
textField.resignFirstResponder()
if let index = self.txtOtp.index(where:{$0 === textField}) {
if index < 3 {
self.txtOtp[index + 1].becomeFirstResponder()
}
}
}
}
return true
}
**call from UITextfieldDelegate function and make next text field the first responder and no need to add target and remember to set delegates of all text fields in viewDidLoad **
extension ViewController : UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
nextTextFieldToFirstResponder(textField)
return true;
}
func nextTextFieldToFirstResponder(textField: UITextField) {
if textField == emailTextField
{
self.firstNameTextField.becomeFirstResponder()
}
else if textField == firstNameTextField {
self.lastNameTextField.becomeFirstResponder()
}
else if textField == lastNameTextField {
self.passwordTextField.becomeFirstResponder()
}
else if textField == passwordTextField {
self.confirmPassTextField.becomeFirstResponder()
}
else if textField == confirmPassTextField {
self.confirmPassTextField.resignFirstResponder()
}
}
I have tried many codes and finally this worked for me in Swift 3.0 Latest [March 2017]
The "ViewController" class should inherited the "UITextFieldDelegate" for making this code working.
class ViewController: UIViewController,UITextFieldDelegate
Add the Text field with the Proper Tag nuber and this tag number is used to take the control to appropriate text field based on incremental tag number assigned to it.
override func viewDidLoad() {
userNameTextField.delegate = self
userNameTextField.tag = 0
userNameTextField.returnKeyType = UIReturnKeyType.next
passwordTextField.delegate = self
passwordTextField.tag = 1
passwordTextField.returnKeyType = UIReturnKeyType.go
}
In the above code, the "returnKeyType = UIReturnKeyType.next" where will make the Key pad return key to display as "Next" you also have other options as "Join/Go" etc, based on your application change the values.
This "textFieldShouldReturn" is a method of UITextFieldDelegate controlled and here we have next field selection based on the Tag value incrementation
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
return true;
}
return false
}
Use textFieldShouldBeginEditing method
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70),
animated:true)
if(textField == firstDigit){
textField.becomeFirstResponder()
secondDigit.resignFirstResponder()
}
else if(textField == secondDigit){
textField.becomeFirstResponder()
thirdDigit.resignFirstResponder()
}
else if(textField == thirdDigit){
//textField.becomeFirstResponder()
fourthDigit.becomeFirstResponder()
}
return true;
}
//MARK:- IBOutlets
#IBOutlet weak var tfFirstDigit: UITextField!
#IBOutlet weak var tfSecondDigit: UITextField!
#IBOutlet weak var tfThirdDigit: UITextField!
#IBOutlet weak var tfFourthDigit: UITextField!
//MARK:- view Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
tfFirstDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
tfSecondDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
tfThirdDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
tfFourthDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
}
//MARK:- Text Field Delegate methods
#objc func textFieldDidChange(textField: UITextField){
let text = textField.text
if (text?.utf16.count)! >= 1{
switch textField{
case tfFirstDigit:
tfSecondDigit.becomeFirstResponder()
case tfSecondDigit:
tfThirdDigit.becomeFirstResponder()
case tfThirdDigit:
tfFourthDigit.becomeFirstResponder()
case tfFourthDigit:
tfFourthDigit.resignFirstResponder()
default:
break
}
}else{
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
let do something different using IQKeyboardManager.It work like charm. Do not forget set delegate for every text field.
//MARK:- TextField delegate methods
#objc func textFieldDidChange(textField: UITextField){
if textField.text!.count == 1{
if IQKeyboardManager.shared().canGoNext{
IQKeyboardManager.shared().goNext()
}
}else{
if IQKeyboardManager.shared().canGoPrevious{
IQKeyboardManager.shared().goPrevious()
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string == " "{
return false
}else if string.isEmpty{
return true
}else if textField.text!.count == 1{
textField.text = string
if IQKeyboardManager.shared().canGoNext{
IQKeyboardManager.shared().goNext()
}
return false
}
return true
}
for swift 3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// On inputing value to textfield
if ((textField.text?.characters.count)! < 1 && string.characters.count > 0){
let nextTag = textField.tag + 1;
// get next responder
let nextResponder = textField.superview?.viewWithTag(nextTag);
textField.text = string;
if (nextResponder == nil){
textField.resignFirstResponder()
}
nextResponder?.becomeFirstResponder();
return false;
}
else if ((textField.text?.characters.count)! >= 1 && string.characters.count == 0){
// on deleting value from Textfield
let previousTag = textField.tag - 1;
// get next responder
var previousResponder = textField.superview?.viewWithTag(previousTag);
if (previousResponder == nil){
previousResponder = textField.superview?.viewWithTag(1);
}
textField.text = "";
previousResponder?.becomeFirstResponder();
return false;
}
return true;
}
I have four textFields and each takes only one character, similar to the lock screen. I can input and move from one textField to next textField.
Problem is when I want to delete and move to previous textField, I don't know when the delete button is clicked.
I'm using:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
to input and move from one textField to another.
How do I know when the delete key is pressed?
edit/update:
Xcode 11 • Swift 5.2 or later
Follow up on this question and Swift 5 syntax can be found on this post
Original answer
Swift 1.x
Following up Istvan answer, you need to post a notification when the deleteBackward is pressed:
class DigitField: UITextField {
override func deleteBackward() {
super.deleteBackward()
NSNotificationCenter.defaultCenter().postNotificationName("deletePressed", object: nil)
}
}
Then inside viewDidLoad() you add an observer as follow:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "goPrevious", name: "deletePressed", object: nil)
and your method:
func goPrevious() {
if secondDigit.isFirstResponder() {
secondDigit.enabled = false
firstDigit.enabled = true
firstDigit.becomeFirstResponder()
} else if thirdDigit.isFirstResponder() {
thirdDigit.enabled = false
secondDigit.enabled = true
secondDigit.becomeFirstResponder()
} else if fourthDigit.isFirstResponder() {
fourthDigit.enabled = false
thirdDigit.enabled = true
thirdDigit.becomeFirstResponder()
}
}
Select your text field and connect it to your DigitField
You need to connect each text field to an IBAction (using sent events editing changed)
The view controller code should look like this:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var firstDigit: UITextField!
#IBOutlet weak var secondDigit: UITextField!
#IBOutlet weak var thirdDigit: UITextField!
#IBOutlet weak var fourthDigit: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "goPrevious", name: "deletePressed", object: nil)
firstDigit.secureTextEntry = true
secondDigit.secureTextEntry = true
thirdDigit.secureTextEntry = true
fourthDigit.secureTextEntry = true
firstDigit.keyboardType = .DecimalPad
secondDigit.keyboardType = .DecimalPad
thirdDigit.keyboardType = .DecimalPad
fourthDigit.keyboardType = .DecimalPad
firstDigit.becomeFirstResponder()
secondDigit.enabled = false
thirdDigit.enabled = false
fourthDigit.enabled = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func goPrevious() {
if secondDigit.isFirstResponder() {
secondDigit.enabled = false
firstDigit.enabled = true
firstDigit.becomeFirstResponder()
} else if thirdDigit.isFirstResponder() {
thirdDigit.enabled = false
secondDigit.enabled = true
secondDigit.becomeFirstResponder()
} else if fourthDigit.isFirstResponder() {
fourthDigit.enabled = false
thirdDigit.enabled = true
thirdDigit.becomeFirstResponder()
}
}
// You need to connect each text field to an IBAction (using sent events editing changed) –
#IBAction func firstChanged(sender: UITextField) {
if let digitOne = sender.text.toInt() {
println(digitOne)
sender.enabled = false
secondDigit.enabled = true
secondDigit.becomeFirstResponder()
} else {
sender.text = ""
}
}
#IBAction func secondChanged(sender: UITextField) {
if let digitTwo = sender.text.toInt() {
println(digitTwo)
sender.enabled = false
thirdDigit.enabled = true
thirdDigit.becomeFirstResponder()
} else {
sender.text = ""
}
}
#IBAction func thirdChanged(sender: UITextField) {
if let digitThree = sender.text.toInt() {
println(digitThree)
sender.enabled = false
fourthDigit.enabled = true
fourthDigit.becomeFirstResponder()
} else {
sender.text = ""
}
}
#IBAction func fourthChanged(sender: UITextField) {
if let digitFour = sender.text.toInt() {
println(digitFour)
sender.enabled = false
} else {
sender.text = ""
}
}
}
Subclass UITextField as follows:
import UIKit
class MyTextField: UITextField {
override func deleteBackward() {
super.deleteBackward()
println("Delete button was tapped")
}
}
Then change the class of your text field to MyTextField. That should do it!
There is a possible solution in this answer to a similar question. You can subclass your UITextField and then override this method in your subclass:
(BOOL)keyboardInputShouldDelete:(UITextField *)textField
Event this code is in Objective-C, it should show you the proper use of this method.
Use replacementString in function to know whats happening in UItextField
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (string.length == 0) {
//delete
} else {
//any other key
}
return YES;
}
I hope my answer will help you:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
println("Backspace was pressed")
}
return true
}
I am trying to check when a text field changes, equivalent too the function used for textView - textViewDidChange so far I have done this:
func textFieldDidBeginEditing(textField: UITextField) {
if self.status.text == "" && self.username.text == "" {
self.topRightButton.enabled = false
} else {
self.topRightButton.enabled = true
}
}
Which kind of works, but the topRightButton is enabled as soon as the text field is pressed on, I want it to be enabled only when text is actually typed in?
SWIFT
Swift 4.2
textfield.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)
and
#objc func textFieldDidChange(_ textField: UITextField) {
}
SWIFT 3 & swift 4.1
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)
and
func textFieldDidChange(_ textField: UITextField) {
}
SWIFT 2.2
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)
and
func textFieldDidChange(textField: UITextField) {
//your code
}
OBJECTIVE-C
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
and textFieldDidChange method is
-(void)textFieldDidChange :(UITextField *) textField{
//your code
}
You can make this connection in interface builder.
In your storyboard, click the assistant editor at the top of the screen (two circles in the middle).
Ctrl + Click on the textfield in interface builder.
Drag from EditingChanged to inside your view controller class in the assistant view.
Name your function ("textDidChange" for example) and click connect.
Swift 5.0
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
for: .editingChanged)
and handle method:
#objc func textFieldDidChange(_ textField: UITextField) {
}
Swift 4.0
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
and handle method:
#objc func textFieldDidChange(_ textField: UITextField) {
}
Swift 3.0
textField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
and handle method:
func textFieldDidChange(textField: UITextField) {
}
The way I've handled it so far: in UITextFieldDelegate
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
// text hasn't changed yet, you have to compute the text AFTER the edit yourself
let updatedString = (textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)
// do whatever you need with this updated string (your code)
// always return true so that changes propagate
return true
}
Swift4 version
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
return true
}
Swift 3
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(sender:)), for: UIControlEvents.editingChanged)
textField(_:shouldChangeCharactersIn:replacementString:) worked for me in Xcode 8, Swift 3 if you want to check every single keypress.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Whatever code you want to run here.
// Keep in mind that the textfield hasn't yet been updated,
// so use 'string' instead of 'textField.text' if you want to
// access the string the textfield will have after a user presses a key
var statusText = self.status.text
var usernameText = self.username.text
switch textField{
case self.status:
statusText = string
case self.username:
usernameText = string
default:
break
}
if statusText == "" && usernameText == "" {
self.topRightButton.enabled = false
} else {
self.topRightButton.enabled = true
}
//Return false if you don't want the textfield to be updated
return true
}
Swift 3.0.1+ (Some of the other swift 3.0 answers are not up to date)
textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
func textFieldDidChange(_ textField: UITextField) {
}
You can use this delegate method from UITextFieldDelegate. It fires with every character change.
(Objective C) textField:shouldChangeCharactersInRange:replacementString:
(Swift) textField(_:shouldChangeCharactersInRange:replacementString:)
However THIS ONLY FIRES BEFORE a change is made (indeed, a change is only made if you do return true from here).
Swift 4
Conform to UITextFieldDelegate.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// figure out what the new string will be after the pending edit
let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
// Do whatever you want here
// Return true so that the change happens
return true
}
Maybe use RxSwift ?
need
pod 'RxSwift', '~> 3.0'
pod 'RxCocoa', '~> 3.0'
add imports obviously
import RxSwift
import RxCocoa
So u have a textfield : UITextField
let observable: Observable<String?> = textField.rx.text.asObservable()
observable.subscribe(
onNext: {(string: String?) in
print(string!)
})
U have other 3 methods..
onError
onCompleted
onDisposed
onNext
Swift 4
textField.addTarget(self, action: #selector(textIsChanging), for: UIControlEvents.editingChanged)
#objc func textIsChanging(_ textField:UITextField) {
print ("TextField is changing")
}
If you want to make a change once the user has typed in completely (It will be called once user dismiss keyboard or press enter).
textField.addTarget(self, action: #selector(textDidChange), for: UIControlEvents.editingDidEnd)
#objc func textDidChange(_ textField:UITextField) {
print ("TextField did changed")
}
You should follow this steps:
Make a Outlet reference to the textfield
AssignUITextFieldDelegate to the controller class
Configure yourTextField.delegate
Implement whatever function you need
Sample code:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var yourTextFiled : UITextField!
override func viewDidLoad() {
super.viewDidLoad()
yourTextFiled.delegate = self
}
func textFieldDidEndEditing(_ textField: UITextField) {
// your code
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// your code
}
.
.
.
}
In case it is not possible to bind the addTarget to your UITextField, I advise you to bind one of them as suggested above, and insert the code for execution at the end of the shouldChangeCharactersIn method.
nameTextField.addTarget(self, action: #selector(RegistrationViewController.textFieldDidChange(_:)), for: .editingChanged)
#objc func textFieldDidChange(_ textField: UITextField) {
if phoneNumberTextField.text!.count == 17 && nameTextField.text!.count > 0 {
continueButtonOutlet.backgroundColor = UIColor(.green)
} else {
continueButtonOutlet.backgroundColor = .systemGray
}
}
And in call in shouldChangeCharactersIn func.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else {
return true
}
let lastText = (text as NSString).replacingCharacters(in: range, with: string) as String
if phoneNumberTextField == textField {
textField.text = lastText.format("+7(NNN)-NNN-NN-NN", oldString: text)
textFieldDidChange(phoneNumberTextField)
return false
}
return true
}
There's now a UITextField delegate method available on iOS13+
optional func textFieldDidChangeSelection(_ textField: UITextField)
txf_Subject.addTarget(self, action:#selector(didChangeFirstText), for: .editingChanged)
#objc func didChangeText(textField:UITextField) {
let str = textField.text
if(str?.contains(" "))!{
let newstr = str?.replacingOccurrences(of: " ", with: "")
textField.text = newstr
}
}
#objc func didChangeFirstText(textField:UITextField) {
if(textField.text == " "){
textField.text = ""
}
}
Swift 4.2
write this in viewDidLoad
// to detect if TextField changed
TextField.addTarget(self, action: #selector(textFieldDidChange(_:)),
for: UIControl.Event.editingChanged)
write this outside viewDidLoad
#objc func textFieldDidChange(_ textField: UITextField) {
// do something
}
You could change the event by UIControl.Event.editingDidBegin or what ever you want to detect.
This is how you can add a textField text change listener using Swift 3:
Declare your class as UITextFieldDelegate
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textField.addTarget(self, action: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:)), for: UIControlEvents.editingChanged)
}
Then just traditionally add a textFieldShouldEndEditing function:
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { // do stuff
return true
}
Just in case you are interested in a SwiftUI solution, this it's working for me:
TextField("write your answer here...",
text: Binding(
get: {
return self.query
},
set: { (newValue) in
self.fetch(query: newValue) // any action you need
return self.query = newValue
}
)
)
I have to say it's not my idea, I read it in this blog: SwiftUI binding: A very simple trick
You can manage editing either by "shouldchangecharacter" delgate or "begin and end" textfield delegate. Don't forget to set delegate
I am manage to do this using "did begin and end editing" below.
//MARK:- TextViewDelegate
extension ViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
let count = self.tfEmail.text?.count ?? 0
if textField == self.tfEmail {
if count == 0{
//Empty textfield
}else{
//Non-Empty textfield
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.tfEmail{
//when user taps on textfield
}
}
}
swift 4
In viewDidLoad():
//ADD BUTTON TO DISMISS KEYBOARD
// Init a keyboard toolbar
let toolbar = UIView(frame: CGRect(x: 0, y: view.frame.size.height+44, width: view.frame.size.width, height: 44))
toolbar.backgroundColor = UIColor.clear
// Add done button
let doneButt = UIButton(frame: CGRect(x: toolbar.frame.size.width - 60, y: 0, width: 44, height: 44))
doneButt.setTitle("Done", for: .normal)
doneButt.setTitleColor(MAIN_COLOR, for: .normal)
doneButt.titleLabel?.font = UIFont(name: "Titillium-Semibold", size: 13)
doneButt.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
toolbar.addSubview(doneButt)
USDTextField.inputAccessoryView = toolbar
Add this function:
#objc func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}