在空的UITextField中检测退格


126

有什么方法可以检测iPhone键盘上的Backspace/ Delete键何时UITextField为空?我想知道什么时候Backspace才被按下UITextField


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

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

收到此通知(handleTextFieldChanged称为函数),但是当我Backspace在空白字段中按键时仍未收到此通知。有任何想法吗?


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


我认为您的意思可能是“仅当UITextField为空时”而不是“仅当键盘为空时” ...?
史蒂夫·哈里森

@史蒂夫·哈里森:谢谢。更新了。
marcc

我正在尝试做类似的事情,那么您得到了什么解决方案?我正在做的是滚动视图中的文本字段,当我键入一些文本时,会显示建议,而当我单击其中的一个时,标签对象将放置在文本字段的左侧。在此先感谢:D
面团

2011/11溶液:为空的UITextField使用运行时诡计bjhomer.blogspot.com/2011/11/...
Jano的

4
渴望如此琐碎的事情。这应该不难。
亚当·怀特

Answers:


36

这可能需要很长时间,但可能会起作用。尝试将文本字段的文本设置为零宽度的空格字符\u200B。在显示为空的文本字段上按退格键时,实际上会删除您的空格。然后,您可以重新插入空间。

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


3
@Andrew,这是我决定采用的方法。花费了一些代码,但是肯定有效。感谢您的帮助,而不是试图告诉我我做错了什么。
marcc 2010年

2
这项技术可能适用于iPhone 3.1.3以上版本,但它确实存在漏洞,并且可能会在将来的版本等方面失效。我想我找到了一种更干净,更稳定的解决方案,用于检测iPhone / iOS上的删除键盘按键
ma11hew28 2011年

6
我可以确认,如果用户设法将光标移到空白处,则不会检测到删除。如果你能弄清楚如何解决这个问题,那么你也应该继承UITextField和落实canPerformAction:withSender:,以return NO用于select:selectAll:行动当文本等于字符串@"\u200B"
ma11hew28 2011年

2
问题是这不适用于安全测试字段。有什么想法如何处理吗?
凯尔·克莱格

7
抱歉,这个主意不好。这是令人难以置信的骇客行为,不应被30次左右的投票所接受。我将把UITextField子类化,就像其他评论者提到的那样。
Brian Sachetta 2015年

155

斯威夫特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

现在,只需将MyTextFieldDelegate添加到您的UIViewController并将UITextFields myDelegate设置为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");
}

9
这是最好的解决方案。接受的答案是骇客。目标C基于子类,此解决方案可以正确地使用它来解决问题。
TJ

ViewController 如果我将文本字段的委托设置为的子类UITextField而不是UIViewController
rohan-patel 2014年

4
由于似乎是Apple Bug,目前在ios8中显然不起作用:devforums.apple.com/message/1045312#1045312
chug2k 2014年

1
我在我的答案中使用了针对ios8错误的解决方法,它奏效了。这对于正在寻找解决方案的人员可能很有用。
2014年

1
deleteBackward()如果您加入return false,将不会被致电textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
igrrik '19

41

更新:有关重写示例,请参见JacobCaraballo的答案-[UITextField deleteBackward]

签出UITextInput,特别是UIKeyInput有一个deleteBackward委托方法,该方法总是在按下Delete键时被调用。如果您正在做简单的事情,那么您可能会考虑仅继承子类UILabel并使其符合UIKeyInput协议,如SimpleTextInput和此iPhone UIKeyInput Example所做的那样注意:UITextInput及其亲戚(包括UIKeyInput)仅在iOS 3.2及更高版本中可用。


2
这是正确的答案。UITextField的一个快速子类可以使您轻松自如。
2013年

刚刚在下面发现了雅各布的答案。它给出了一个详细的例子。
2013年

值得注意的是,此答​​案在iOS 5中不起作用。如果文本字段为空,则按Backspace键不会调用此方法。
jfeldman 2013年

31

代码如下:

@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

最后,不要忘记将“文本字段”的“自定义类”属性更改为“ MyTextField”


5
这应该是公认的答案。干净,并实际上回答了问题。
Ryan Romanchuk

1
它确实回答了这个问题……只要您的目标是iOS 6.0以上。不幸的是,在iOS 5上,从未从未在您的子类上调用deleteBackward。
BJ荷马

2
BJ Homer,93%的设备都在iOS 6上,因此不针对iOS 5通常不是什么大问题。
乔纳森。

2
我很高兴我一直向下滚动足以找到它。100%做到这一点今天在IOS 7正道
MusiGenesis

对于这个问题,我已经看到许多其他答案,它们只是变通办法,几乎没有一个能解决在空白字段中按Backspace键的情况。但是,这是完美的,而且是一种非常干净的方法。
Christopher Hannah'9

19

迅捷的实现:

import UIKit

protocol PinTexFieldDelegate : 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? PinTexFieldDelegate {
            pinDelegate.didPressBackspace(self)
        }
    }
}

谢谢,我是否可以知道此方法是苹果推荐的方法,还是很容易破解。似乎没有记录的文本字段。
kalpesh jetani'May

使用textView为我工作。感谢您的分享;)
Edouard Barbier

在文本字段为空并单击退格键时为我工作。
罗摩克里希纳

17

我已经找到了比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。但它的正确工作;)


1
仰泳未设置为'\ b'。但是,如果您仔细调试,则会看到“ \ 0”。所以我得到的结果0是在strcmp方法中两个值相等。const char * stoke = [text cStringUsingEncoding:NSASCIIStringEncoding]; const char * backstroke =“ \ 0”; //等于\ b->“ \ x08”; strcmp(仰泳,冲程);
李允(Yoon Lee)

同样,根据strcmp(s1,s2)的定义,如果s1 == s2,则大于0 s1在字典上大于s2,反之亦然。
李允(Yoon Lee)

我不知道这怎么可能行得通?@YoonLee:你不是说strcmp回报-1, 0, or 1吗?那是我的理解。
chug2k

1
在textview中,即使没有文本,UITextField也总是检测到退格
LolaRun 2014年

2
这是一种UITextViewDelegate方法,不是UITextFieldDelegate
pkamb


11

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

目标C:

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

斯威夫特:

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

Tx-正是我需要的。但是记录在哪里?
安迪·温斯坦

9

Niklas Alvaeus的回答也帮助我解决了类似的问题

我将输入限制为特定的字符集,但它忽略了退格键。所以我range.length == 1在修整之前检查了它NSString。如果是真的,我只返回字符串,而不修剪它。见下文

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

该方法期望布尔值而不是字符串作为返回值
Pascalius 2012年

4

对于那些对Jacob答案有疑问的人,我将我的textfield子类实现为以下代码,并且效果很好!

#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

您可以快速编写代码吗?我收到一些错误“对protoco uikeyinput进行了冗余确认”
Raj Aggrawal

4

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

需要添加 UITextFieldDelegate

yourTextField.delegate =自我(必须)

迅速:

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

目标C:

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

5
这甚至都不要求我。其他所有委托方法均在发生事件的时间和时间被调用。
Hemang '18年

谢谢配合。你钉它:)
MS。

@ McDonal_11我认为您忘记设置textfield.delegate =文本字段的自身。
Himanshu padia

我已经设定。其他UITextField的委托方法工作正常。仅此一个无效。我想念什么??
McDonal_11年

3

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

这可以通过与Jacob Caraballo的答案类似的方式来完成。但是在Jacob的答案中,如果在UITextField退格时还剩一个字符,那么到接收到委托消息时,该字符UITextField将为空,因此您可以有效地检测backspace到最多包含一个字符的文本字段。

实际上,如果您想检测backspace到一个UITextField完全为零的字符(空),则应delegate在调用之前将消息发送到super deleteBackward。例如:

#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

2
为了使它在iOS8中工作(存在导致该委托方法永不被调用的错误),请参阅以下答案:stackoverflow.com/a/25862878/893101。有关ios8错误的更多详细信息:devforums.apple.com/message/1009150#1009150
pIkEL 2014年

2

在iOS 6中,当按下Backspace(包括字段为空)时,将在UITextField上调用deleteBackward方法。因此,您可以继承UITextField并提供您自己的deleteBackward实现(也可以调用super的实现)。

不过我仍然支持iOS 5,因此我需要结合Andrew的答案和这个。


1

:)仅用于标题“检测退格”,在此使用 UIKeyboardTypeNumberPad

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

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

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

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


任何键盘类型都一样。
ma11hew28

如何将这种方法连接到我的文本字段?
user230910

1

shouldChangeCharactersInRange我建议不要执行以下操作:建议预先构造文本字段中的内容,或者弄清楚在方法中输入了什么特殊字符:

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

这样,您就可以在当前操作完成后直接调用方法。最酷的是,等到它完成时,修改后的值将已经在中UITextField。此时,您可以仅检查其长度和/或根据其中的内容进行验证。


1

子类化UITextField在iOS 8.3上对我不起作用,从未调用deleteBackward。

这是我使用的解决方案,适用于所有iOS 8版本,也应适用于其他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
}

1

在.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;
}   

改进的格式


1

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

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

1

最流行的答案是缺少一件事-检测文本字段是否为空的能力。

也就是说,当覆盖TextField子类的deleteBackwards()方法时,您仍然不知道文本字段是否已经为空。(在deleteBackwards()之前和之后,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...
        }
    }
}

1

Swift 5.1文本位的综合处理程序:

  • 假设您有textField的出口集合(以及连接的委托)

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

2步

final class ViewController: UIViewController {

    @IBOutlet private var textFields: [MyTextField]!

    override func viewDidLoad() {
        super.viewDidLoad()
        textFields.forEach {
            $0.delegate = self
            $0.myDelegate = self
        }
    }
}

3步

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

0

这是我基于@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 
        }

0

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

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

-2

像这样:

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

当然,哈希是针对一个字符串的。


-2

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


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

2
也许有一点解释放在哪里?
Alex Cio 2014年

-4

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,并且一个空字符串对象替换了该单个字符。”


1
这个问题是关于UITextField,不是UITextView
ma11hew28
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.