How to Create Otp Verification Screen and Detect Delete Backward on Multiple Uitextfield Is Swift

How to create OTP verification screen and detect delete backward on multiple uitextfield is Swift

Instead of fixing that code I prefer to create a custom text field that would inform when the deleteBackward key is pressed. So first subclass a UITextField:



import UIKit
class SingleDigitField: UITextField {
// create a boolean property to hold the deleteBackward info
var pressedDelete = false
// customize the text field as you wish
override func willMove(toSuperview newSuperview: UIView?) {
keyboardType = .numberPad
textAlignment = .center
backgroundColor = .blue
isSecureTextEntry = true
isUserInteractionEnabled = false
}
// hide cursor
override func caretRect(for position: UITextPosition) -> CGRect { .zero }
// hide selection
override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] { [] }
// disable copy paste
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { false }
// override deleteBackward method, set the property value to true and send an action for editingChanged
override func deleteBackward() {
pressedDelete = true
sendActions(for: .editingChanged)
}
}

Now in your ViewCOntroller:

import UIKit

class ViewController: UIViewController {
// connect the textfields outlets
@IBOutlet weak var firstDigitField: SingleDigitField!
@IBOutlet weak var secondDigitField: SingleDigitField!
@IBOutlet weak var thirdDigitField: SingleDigitField!
@IBOutlet weak var fourthDigitField: SingleDigitField!
override func viewDidLoad() {
super.viewDidLoad()
// add a target for editing changed for each field
[firstDigitField,secondDigitField,thirdDigitField,fourthDigitField].forEach {
$0?.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
}
// make the firsDigitField the first responder
firstDigitField.isUserInteractionEnabled = true
firstDigitField.becomeFirstResponder()
}
// here you control what happens to each change that occurs to the fields
@objc func editingChanged(_ textField: SingleDigitField) {
// check if the deleteBackwards key was pressed
if textField.pressedDelete {
// reset its state
textField.pressedDelete = false
// if the field has text empty its content
if textField.hasText {
textField.text = ""
} else {
// otherwise switch the field, resign the first responder and activate the previous field and empty its contents
switch textField {
case secondDigitField, thirdDigitField, fourthDigitField:
textField.resignFirstResponder()
textField.isUserInteractionEnabled = false
switch textField {
case secondDigitField:
firstDigitField.isUserInteractionEnabled = true
firstDigitField.becomeFirstResponder()
firstDigitField.text = ""
case thirdDigitField:
secondDigitField.isUserInteractionEnabled = true
secondDigitField.becomeFirstResponder()
secondDigitField.text = ""
case fourthDigitField:
thirdDigitField.isUserInteractionEnabled = true
thirdDigitField.becomeFirstResponder()
thirdDigitField.text = ""
default:
break
}
default: break
}
}
}
// make sure there is only one character and it is a number otherwise delete its contents
guard textField.text?.count == 1, textField.text?.last?.isWholeNumber == true else {
textField.text = ""
return
}
// switch the textField, resign the first responder and make the next field active
switch textField {
case firstDigitField, secondDigitField, thirdDigitField:
textField.resignFirstResponder()
textField.isUserInteractionEnabled = false
switch textField {
case firstDigitField:
secondDigitField.isUserInteractionEnabled = true
secondDigitField.becomeFirstResponder()
case secondDigitField:
thirdDigitField.isUserInteractionEnabled = true
thirdDigitField.becomeFirstResponder()
case thirdDigitField:
fourthDigitField.isUserInteractionEnabled = true
fourthDigitField.becomeFirstResponder()
default: break
}
case fourthDigitField:
fourthDigitField.resignFirstResponder()
default: break
}
}
}

Xcode 12 sample project

How to move cursor from one text field to another automatically in swift ios programmatically?

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 = ""
}
}

Detect backspace Event in UITextField

Swift 4.2

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let char = string.cString(using: String.Encoding.utf8) {
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
}
}
return true
}

Older Swift version

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
}

Text field one character and automatic change to next text field?Not working?

First, make sure the delegate is set for all four text fields. Also make sure your outlets are connected properly.

Next, change your use of isEqual: to ==. You actually do want to use == here since you want to compare pointers, not see if the two objects are logically equal.

You also have a problem if the user pastes text into the text field. The user can easily enter more than one character.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (string.length > 0) {
textField.text = [string substringToIndex:1];
if (textField == self.firstOTP) {
[self.secondOTP becomeFirstResponder];
} else if (textField == self.secondOTP) {
[self.thirdOTP becomeFirstResponder];
} else if (textField == self.thirdOTP) {
[self.fourthOTP becomeFirstResponder];
} else {
[textField resignFirstResponder];
}

return NO;
}

return YES;
}

How to know when the delete button on decimal pad is clicked

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

Sample Image

You need to connect each text field to an IBAction (using sent events editing changed)

Sample Image

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 = ""
}
}
}

UITextField: Password Implementation

Need to give tag into UITextView in StoryBoard.

Like textField1 = 101,textField2 = 102,textField3 = 103,textField4 = 104

Sample Image

Put following code into your viewcontroller.

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
BOOL shouldDelete = YES;

if ([textField.text length] == 0 && [textField.text isEqualToString:@""]) {
long tagValue = textField.tag - 1;
UITextField *txtField = (UITextField*) [self.view viewWithTag:tagValue];

[txtField becomeFirstResponder];
}
return shouldDelete;
}

Following code for the next UItextField Focus.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// This allows numeric text only, but also backspace for deletes
if (string.length > 0 && ![[NSScanner scannerWithString:string] scanInt:NULL])
return NO;

if ([textField.text length] == 0) {
[self performSelector:@selector(changeTextFieldFocusToNextTextField:) withObject:textField afterDelay:0.3];
}

return YES;
}

-(void)changeTextFieldFocusToNextTextField:(UITextField*)textField{
long tagValue = textField.tag + 1;
UITextField *txtField = (UITextField*) [self.view viewWithTag:tagValue];
[txtField becomeFirstResponder];
}

Sample Output

Sample Image

Grab the solution demo project



Related Topics



Leave a reply



Submit