在空的 UITextField 中检测退格

有没有什么方法可以检测到当 UITextField是空的时候,在 iPhone 键盘上按下 Backspace/Delete键?我想知道当按下 Backspace只有当 UITextField是空的。


根据@Alex Reynolds 在评论中的建议,我在创建文本字段时添加了以下代码:

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTextFieldChanged:)
name:UITextFieldTextDidChangeNotification
object:searchTextField];

收到此通知(调用 handleTextFieldChanged函数) ,但在空字段中按下 Backspace键时仍未收到通知。有什么想法吗?


这个问题似乎有些混乱。我希望在按下 Backspace键时收到通知。就是这样。但是,当 UITextField已经为空时,解决方案也必须起作用。

81288 次浏览

这可能是一个很长的机会,但它可以工作。尝试将文本字段的文本设置为零宽度空间字符 \u200B。如果在显示为空的文本字段上按退格键,它实际上会删除您的空格。然后你可以重新插入空间。

如果用户设法将插入符号移动到空格的左侧,则可能无法工作。

试试 delegate

- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {

然后检查当 backspace被击中时 range.length == 1是否出现这种情况。

尼克拉斯 · 阿尔维的回答帮助我解决了一个类似的问题

我限制了一个特定字符集的输入,但是忽略了后退空间。 所以我让它在修剪 NSString之前检查 range.length == 1。如果它是真的,我只是返回字符串并且不修剪它。请看下面

 - (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);
}
}

更新: 有关覆盖 -[UITextField deleteBackward]的示例,请参见 JacobCaraballo 的回答

看看 UITextInput,特别是 UIKeyInput有一个 deleteBackward委托方法,按下删除键时 UIKeyInput0会被调用。如果您正在做一些简单的事情,那么您可以考虑只是子类化 UILabel并使其符合 UIKeyInput协议,就像 UIKeyInput1和这个 UIKeyInput2所做的那样。UIKeyInput3

:)只是为了标题“检测后退空间”,在这里我使用 UIKeyboardTypeNumberPad

今晚我也遇到了同样的问题,下面是我的代码来找出答案:

- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
NSLog([NSString stringWithFormat:@"%d", [string length]]);
}

因为使用 UIKeyboardTypeNumberPad时,用户只能输入数字或退格键,所以当字符串长度为0时,它必须是退格键。

希望以上内容能对你有所帮助。

我找到了比 subclass解决方案更简单的方法,即使它有点奇怪,但是它工作正常。

- (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;
}

比较的结果是 -8,这有点奇怪。也许我会在 C Programming的某个点上出错。但它的工作是正确的;)

大概是这样:

- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
if (![text hash] && ![textField.text length])
[self backspaceInEmptyTextField];
}

当然,哈希表示一个字符串。

在 iOS6中,当按退格键时(包括该字段为空时) ,会对 UITextField 调用 delete teBackward 方法。因此,您可以子类化 UITextField 并提供自己的 delete teBackward 实现(也调用 super)

我仍然支持 iOS5,所以我需要一个安德鲁的答案和这个组合。

与其试图预先构建文本字段中的内容,或者弄清楚在 shouldChangeCharactersInRange方法中输入了什么特殊字符,我建议做以下事情:

[self performSelector:@selector(manageSearchResultsDisplay)
withObject:nil
afterDelay:0];

这允许您在当前操作完成后直接调用方法。这样做的好处是,当它完成时,修改后的值已经在 UITextField中了。在这一点上,您可以检查它的长度和/或根据它的内容进行验证。

UITextViewDelegate:

- (BOOL)               textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
if(text isEqualToString:@"");
{
NSLog(@"press backspace.");
}
}

对我来说没问题。

更新中文简体拼音及中文手写输入法:

- (BOOL)               textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
if (range.length > 0 && [text isEqualToString:@""]) {
NSLog(@"press Backspace.");
}
return YES;
}

文件上写着:

如果用户按下 deleteKey,范围的长度为1,一个空字符串对象将替换该单个字符

迅捷4:


亚类 UITextField:

// MyTextField.swift
import UIKit


protocol MyTextFieldDelegate: AnyObject {
func textFieldDidDelete()
}


class MyTextField: UITextField {


weak var myDelegate: MyTextFieldDelegate?


override func deleteBackward() {
super.deleteBackward()
myDelegate?.textFieldDidDelete()
}


}

实施方法:

// 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")
}


}

目标 C:


亚类 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

现在简单地把 MyTextField 委托加到你的 UIViewController上,然后把 UITextFields我的代表设成 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");
}

代码如下:

@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

最后,请忘记将 Text Field 的 Custom Class 属性更改为“ MyTextField”

保持简单在这里是唯一的条件,你需要检查

     if (range.length==1)

我发现检测退格的最佳用途是当用户在空 UITextField中按下退格时进行检测。例如,如果您在邮件应用程序中有“冒泡”收件人,当您在 UITextField中点击退格键时,它会选择最后一个“冒泡”收件人。

这可以用与 Jacob Caraballo 的回答类似的方法来完成。但是在雅各布的回答中,如果当你点击退格键时,UITextField还剩下一个字符,那么当收到委托消息时,UITextField已经是空的,所以你实际上是在一个最多只有一个字符的文本字段上检测到了 backspace

实际上,如果您希望在 UITextField上检测到字符数为零(空)的 backspace,那么您应该在调用 super deleteBackward之前将消息发送给 delegate。例如:

#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

这种文本字段的界面如下:

@protocol MyTextFieldDelegate;


@interface MyTextField : UITextField
@property(nonatomic, weak) id<MyTextFieldDelegate> delegate;
@end


@protocol MyTextFieldDelegate <UITextFieldDelegate>
@optional
-(void)textFieldDidHitBackspaceWithEmptyText:(MyTextField *)textField;
@end
+ (BOOL)detectBackspaceOnly:(NSString *)string
{
for(int i=0 ; i<string.length ; i++){
unichar caract = [string characterAtIndex:i];
if(caract != ' ' && caract != '\n')
return NO;
}


return YES;
}

使用 TextField 委托方法:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

在上面的方法中添加以下代码以检测删除事件

if(textField == YourTextField)
{
if ([string length] == 0 && range.length > 0)
{
// Your Code after deletion of character
}
}

对于那些谁有问题的 雅各布的回答我实现了我的文本字段子类如下,它的工作非常好!

#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

对 UITextField 进行子类化在 iOS 8.3上不起作用,从来没有调用 delete teBackward。

这是我使用的解决方案,适用于所有的 iOS8版本,也应该适用于其他的 iOS 版本

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
}

迅速实施:

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)
}
}
}

请使用下面的代码,它将帮助您检测键盘删除键,即使您的文本字段是空的。

目标 C:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { return YES; }

斯威夫特:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { return true }

是的,当 textField 为空时,使用下面的方法来检测退格。

需要添加 UITextField 委托

分派代表 = self (必须要求)

斯威夫特:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool {
return true
}

目标丙:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
return YES;
}

在.h 文件中添加 UIKeyInput 委托

- (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;
}

改进的格式

我已经实现了类似的解决方案,并做了一些小的改进,这些改进将告诉我,当用户点击退格键时,文本字段是否有任何值。这对我的情况很有用,当我只关注另一个文本字段时,如果文本字段是空的,当退格按下。

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)
}
}

最常见的回答 缺少一样东西ーー检测文本字段是否为空的能力。

也就是说,当重写 TextField 子类的 delete teBackward ()方法时,仍然不知道文本字段是否已经为空。(在 delete teBackward ()之前和之后,textField.text!都是一个空字符串: "")

这是我的改进,删除前检查空白。

1. 创建一个扩展 UITextFieldDelegate 的委托协议

protocol MyTextFieldDelegate: UITextFieldDelegate {
func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool)
}

2. UITextField 子类

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)
}
}

3. 实施新的委托协议

extension MyViewController: MyTextFieldDelegate {
func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool) {
if wasEmpty {
// do what you want here...
}
}
}

综合处理程序的文本字段与单个数字编号的 Swift 5.1:

  • 假设您有 textFields 的出口集合(也有连接的委托)

第一步

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)
}
}

2步

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

以下是我基于@andrew 想法的解决方案:

在某个地方,例如 viewDidLoad

        textField.delegate = self
textField.addTarget(self, action: #selector(valueChanged(_:)), for: .editingDidBegin)

然后

    @objc func valueChanged(_ textField: UITextField) {
textField.text = "\u{200B}"
}


override func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
textField.text = string
if string == "" {
//backpaspace pressed
}

您可以检查文本视图/字段的文本,看看它是否为空,并确保在 should dChangeTextIn 委托方法中替换的文本也为空。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (textView.text == "" && text == "") {
print("Backspace on empty text field.")
}
return true
}

所有的答案都非常有帮助,我不知道为什么每个人都采取协议路线。您可以使用更少的代码来完成这项工作,比如使用-这样的回调函数

Swift 5.0或以上版本

  1. 创建一个自定义 textfield 类来扩展 UITextField 并重写 delete teBackward 函数-

    类 CustomTextField: UITextField {

     var backButtonPressedInEmptyTextField: (()->())?
    
    
    override func deleteBackward() {
    super.deleteBackward()
    if let text = self.text, text.count == 0{
    backButtonPressedInEmptyTextField?()
    print("Back space clicked when textfield is empty")
    }
    }
    

    }

  2. 让我们假设您希望在 ViewController (MyViewController)中基于此进行一些操作。因此,在 MyViewController中,只需要执行以下操作-

    类 MyViewController: UIViewController { @ IBOutlet 弱变量 sampleTextField: CustomTextField! { 设定{ BackButtonPresdedInEmptyTextField = 返回按钮按下() } }

     func backButtonPressed(){
    //do whatever you want
    }
    

    }

我觉得闭合或回调功能,这是更干净。