Is there any way to detect when the Backspace/Delete key is pressed in the iPhone keyboard on a UITextField
that is empty? I want to know when Backspace is pressed only if the UITextField
is empty.
Based on the suggestion from @Alex Reynolds in a comment, I've added the following code while creating my text field:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTextFieldChanged:)
name:UITextFieldTextDidChangeNotification
object:searchTextField];
This notification is received (handleTextFieldChanged
function is called), but still not when I press the Backspace key in an empty field. Any ideas?
There seems to be some confusion around this question. I want to receive a notification when the Backspace key is pressed. That's it. But the solution must also work when the UITextField
is already empty.
Subclass UITextField
:
// MyTextField.swift
import UIKit
protocol MyTextFieldDelegate: AnyObject {
func textFieldDidDelete()
}
class MyTextField: UITextField {
weak var myDelegate: MyTextFieldDelegate?
override func deleteBackward() {
super.deleteBackward()
myDelegate?.textFieldDidDelete()
}
}
Implementation:
// ViewController.swift
import UIKit
class ViewController: UIViewController, MyTextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// initialize textField
let input = MyTextField(frame: CGRect(x: 50, y: 50, width: 150, height: 40))
// set viewController as "myDelegate"
input.myDelegate = self
// add textField to view
view.addSubview(input)
// focus the text field
input.becomeFirstResponder()
}
func textFieldDidDelete() {
print("delete")
}
}
Subclass UITextField
:
//Header
//MyTextField.h
//create delegate protocol
@protocol MyTextFieldDelegate <NSObject>
@optional
- (void)textFieldDidDelete;
@end
@interface MyTextField : UITextField<UIKeyInput>
//create "myDelegate"
@property (nonatomic, assign) id<MyTextFieldDelegate> myDelegate;
@end
//Implementation
#import "MyTextField.h"
@implementation MyTextField
- (void)deleteBackward {
[super deleteBackward];
if ([_myDelegate respondsToSelector:@selector(textFieldDidDelete)]){
[_myDelegate textFieldDidDelete];
}
}
@end
Now simply add MyTextFieldDelegate to your UIViewController
and set your UITextFields
myDelegate to self
:
//View Controller Header
#import "MyTextField.h"
//add "MyTextFieldDelegate" to you view controller
@interface ViewController : UIViewController <MyTextFieldDelegate>
@end
//View Controller Implementation
- (void)viewDidLoad {
//initialize your text field
MyTextField *input =
[[MyTextField alloc] initWithFrame:CGRectMake(0, 0, 70, 30)];
//set your view controller as "myDelegate"
input.myDelegate = self;
//add your text field to the view
[self.view addSubview:input];
}
//MyTextField Delegate
- (void)textFieldDidDelete {
NSLog(@"delete");
}
Update: See JacobCaraballo's answer for an example that overrides -[UITextField deleteBackward]
.
Check out UITextInput
, specifically UIKeyInput
has a deleteBackward
delegate method that always gets called when the delete key is pressed. If you're doing something simple, then you might consider just subclassing UILabel
and making it conform to the UIKeyInput
protocol, as done by SimpleTextInput and this iPhone UIKeyInput Example. Note: UITextInput
and its relatives (including UIKeyInput
) are only available in iOS 3.2 and later.
This may be a long shot but it could work. Try setting the text field's text to a zero width space character \u200B
. When backspace is pressed on a text field that appears empty, it will actually delete your space. Then you can just reinsert the space.
May not work if the user manages to move the caret to the left of the space.
Code like following:
@interface MyTextField : UITextField
@end
@implementation MyTextField
- (void)deleteBackward
{
[super deleteBackward];
//At here, you can handle backspace key pressed event even the text field is empty
}
@end
At last, do forget to change the Custom Class property of the Text Field to "MyTextField"
Swift implementation:
import UIKit
// Extend from PinTextFieldDelegate instead of UITextFieldDelegate in your class
protocol PinTextFieldDelegate : UITextFieldDelegate {
func didPressBackspace(_ textField: PinTextField)
}
class PinTextField: UITextField {
override func deleteBackward() {
super.deleteBackward()
// If conforming to our extension protocol
if let pinDelegate = self.delegate as? PinTextFieldDelegate {
pinDelegate.didPressBackspace(self)
}
}
}
I've founded other way easier than subclass
solution. Even its little bit strange but it works ok.
- (BOOL)textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
const char * _char = [text cStringUsingEncoding:NSUTF8StringEncoding];
int isBackSpace = strcmp(_char, "\b");
if (isBackSpace == -8) {
// is backspace
}
return YES;
}
It's a little bit strange for result of compare is -8. Maybe I'll wrong in some point of C Programming
. But its right work ;)
please use below code it will help you to detect keyboard delete key even if you textfield is empty.
Objective C :
- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { return YES; }
Swift :
func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { return true }
Try the delegate
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
Then check if the range.length == 1
which seems to be the case when backspace
is hit.
Niklas Alvaeus's answer helped me out with a similar issue
I was limiting entry to a specific character set, but it was ignoring backspaces.
So I had it check range.length == 1
before trimming the NSString
. If it is true, I just return the string and don't trim it. See below
- (BOOL) textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
NSCharacterSet *nonNumberSet =
[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."]
invertedSet];
if (range.length == 1) {
return string;
}
else {
return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);
}
}
Yup, use below method to detect backspace, when textField is empty.
Need to add UITextFieldDelegate
yourTextField.delegate = self (MUST REQUIRED)
Swift:
func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { return true }
Objective C:
- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { return YES; }
For the ones who has problems about the Jacob's answer I implemented my textfield subclass as following and it works great!
#import <UIKit/UIKit.h>
@class HTTextField;
@protocol HTBackspaceDelegate <NSObject>
@optional
- (void)textFieldDidBackspace:(HTTextField*)textField;
@end
@interface HTTextField : UITextField<UIKeyInput>
@property (nonatomic, assign) id<HTBackspaceDelegate> backspaceDelegate;
@end
#import "HTTextField.h"
@implementation HTTextField
- (void)deleteBackward {
[super deleteBackward];
if ([self.backspaceDelegate respondsToSelector:@selector(textFieldDidBackspace:)]){
[self.backspaceDelegate textFieldDidBackspace:self];
}
}
- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
BOOL shouldDelete = YES;
if ([UITextField instancesRespondToSelector:_cmd]) {
BOOL (*keyboardInputShouldDelete)(id, SEL, UITextField *) = (BOOL (*)(id, SEL, UITextField *))[UITextField instanceMethodForSelector:_cmd];
if (keyboardInputShouldDelete) {
shouldDelete = keyboardInputShouldDelete(self, _cmd, textField);
}
}
if (![textField.text length] && [[[UIDevice currentDevice] systemVersion] intValue] >= 8) {
[self deleteBackward];
}
return shouldDelete;
}
@end
The best use that I have found for detecting backspace is detecting when the user has pressed backspace in an empty UITextField
. For example, if you have 'bubbled' recipients in the mail app, when you hit backspace in the UITextField
, it selects the last 'bubbled' recipient.
This can be done in a similar way to Jacob Caraballo's answer. But in Jacob's answer, if the UITextField
has one character left when you hit backspace, by the time the delegate message is received, the UITextField
will already be empty, so you're effectively detecting backspace
on a text field with at most one characters.
Actually, if you want to detect backspace
on a UITextField
with exactly zero characters (empty), then you should send the message to the delegate
before the call to super deleteBackward
. For example:
#import "MyTextField.h"
//Text field that detects when backspace is hit with empty text
@implementation MyTextField
#pragma mark - UIKeyInput protocol
-(void)deleteBackward
{
BOOL isTextFieldEmpty = (self.text.length == 0);
if (isTextFieldEmpty) {
if ([self.delegate
respondsToSelector:@selector(textFieldDidHitBackspaceWithEmptyText:)]) {
[self.delegate textFieldDidHitBackspaceWithEmptyText:self];
}
}
[super deleteBackward];
}
@end
The interface for such a text field would look something like this:
@protocol MyTextFieldDelegate;
@interface MyTextField : UITextField
@property(nonatomic, weak) id<MyTextFieldDelegate> delegate;
@end
@protocol MyTextFieldDelegate <UITextFieldDelegate>
@optional
-(void)textFieldDidHitBackspaceWithEmptyText:(MyTextField *)textField;
@end
In iOS 6, the deleteBackward method is called on the UITextField when backspace is pressed, including when the field is empty. So you can subclass UITextField and provide your own deleteBackward implementation (invoking super's as well.)
I'm still supporting iOS 5 though so I'll need a combination of Andrew's answer and this.
In .h file add UIKeyInput delegate
- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
if ([textField isEqual:_txtFirstDigit]) {
}else if([textField isEqual:_txtSecondDigit]) {
[_txtFirstDigit becomeFirstResponder];
}else if([textField isEqual:_txtThirdDigit]) {
[_txtSecondDigit becomeFirstResponder];
}else if([textField isEqual:_txtFourthDigit]) {
[_txtThirdDigit becomeFirstResponder];
}
return YES;
}
improved Formatting
:) just for the title "Detect backspace", where I use UIKeyboardTypeNumberPad
.
I also meet the same question tonight, and following is my code to find it out:
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
NSLog([NSString stringWithFormat:@"%d", [string length]]);
}
Because with UIKeyboardTypeNumberPad
, user can only input Number or backspace, so when the length of string is 0, it must be backspace key.
Hope the above will do some help.
Subclassing UITextField did not work for me on iOS 8.3, deleteBackward was never called.
Here is the solution I used, works on all iOS 8 versions and should work on other iOS versions as well
for textField in textFields {
textField.text = " "
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string == "" && textField.text == " " {
// Do stuff here
return false
}
return true
}
I have implemented the similar solution with minor improvements that will tell me that if the text field has any value while the user has tapped the backspace. This is useful for my case when I should only focus on another text field if the text field is empty when backspace pressed.
protocol MyTextFieldDelegate : UITextFieldDelegate {
func textFieldDidDelete(textField: MyTextField, hasValue: Bool)
}
override func deleteBackward() {
let currentText = self.text ?? ""
super.deleteBackward()
let hasValue = currentText.isEmpty ? false : true
if let delegate = self.delegate as? MyTextFieldDelegate {
delegate.textFieldDidDelete(textField: self, hasValue: hasValue)
}
}
Rather than trying to preconstruct what WILL BE in the text field or figure out what special character has been entered in the shouldChangeCharactersInRange
method, I would suggest doing the following:
[self performSelector:@selector(manageSearchResultsDisplay)
withObject:nil
afterDelay:0];
This allows you to call a method directly after the current operation completes. What's cool about this is that, by the time it completes, the modified value will already be in the UITextField
. At that point, you can just check its length and/or validate based on what's there.
The most poplar answer is missing one thing — the ability to detect whether the text field was empty or not.
That is, when you override the deleteBackwards() method of a TextField subclass, you still don't know whether the text field was already empty. (Both before and after deleteBackwards(), textField.text!
is an empty string: ""
)
Here's my improvement, with a check for emptiness prior to deletion.
protocol MyTextFieldDelegate: UITextFieldDelegate {
func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool)
}
class MyTextField: UITextField {
override func deleteBackward() {
// see if text was empty
let wasEmpty = text == nil || text! == ""
// then perform normal behavior
super.deleteBackward()
// now, notify delegate (if existent)
(delegate as? MyTextFieldDelegate)?.textField(self, didDeleteBackwardAnd: wasEmpty)
}
}
extension MyViewController: MyTextFieldDelegate {
func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool) {
if wasEmpty {
// do what you want here...
}
}
}
Comprehensive handler for textfield with single digit number for Swift 5.1:
protocol MyTextFieldDelegate: class {
func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool)
}
final class MyTextField: UITextField {
weak var myDelegate: MyTextFieldDelegate?
override func deleteBackward() {
let wasEmpty = text == nil || text == ""
// then perform normal behavior
super.deleteBackward()
// now, notify delegate (if existent)
(delegate as? MyTextFieldDelegate)?.textField(self, didDeleteBackwardAnd: wasEmpty)
}
}
final class ViewController: UIViewController {
@IBOutlet private var textFields: [MyTextField]!
override func viewDidLoad() {
super.viewDidLoad()
textFields.forEach {
$0.delegate = self
$0.myDelegate = self
}
}
}
extension ViewController: UITextFieldDelegate, MyTextFieldDelegate {
func textFieldHasChanged(with text: String, _ tag: Int, for textField: UITextField) {
textField.text = text
if let someTextField = (textFields.filter { $0.tag == tag }).first {
someTextField.becomeFirstResponder()
} else {
view.endEditing(true)
}
}
func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool) {
// If the user was pressing backward and the value was empty, go to previous textField
textFieldHasChanged(with: "", textField.tag - 1, for: textField)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Restrict to only digits
let aSet = NSCharacterSet(charactersIn: "0123456789").inverted
let compSepByCharInSet = string.components(separatedBy: aSet)
let numberFiltered = compSepByCharInSet.joined(separator: "")
guard string == numberFiltered, let text = textField.text else { return false }
if text.count >= 1 && string.isEmpty {
// If the user is deleting the value
textFieldHasChanged(with: "", textField.tag - 1, for: textField)
} else {
textFieldHasChanged(with: string, textField.tag + 1, for: textField)
}
return false
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With