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
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 = ""
}
}
}
UITextField: Password Implementation
Need to give tag into UITextView
in StoryBoard
.
Like textField1 = 101,textField2 = 102,textField3 = 103,textField4 = 104
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
Grab the solution demo project
Related Topics
Swift Formatting String to Have 2 Decimal Numbers If It Is Not a Whole Number
Swift - Apply Local CSS to Web View
Xcode 10: a Valid Provisioning Profile for This Executable Was Not Found
iPhone Sdk:How to Play Video Inside a View? Rather Than Fullscreen
When Should Translatesautoresizingmaskintoconstraints Be Set to True
Setting Scroll Position in Uitableview
Crashlytics Is Not Sending Crash Report from Iphone
Uibutton Does Not Work When It in Uiscrollview
How to Make the View Update Instant in Swiftui
How to Access the Real 100Vh on iOS in CSS
Updating to Latest Version of Cocoapods
Simply Mask a Uiview with a Rectangle
Query Available iOS Disk Space with Swift
Wireless iPhone App Distribution - Problem with Itms-Services Protocol
Uibutton in Cell in Collection View Not Receiving Touch Up Inside Event