简体   繁体   English

检测 UITextField 中的退格事件

[英]Detect backspace Event in UITextField

I am searching for solutions on how to capture a backspace event, most Stack Overflow answers are in Objective-C but I need on Swift language.我正在寻找有关如何捕获退格事件的解决方案,大多数 Stack Overflow 答案都在 Objective-C 中,但我需要 Swift 语言。

First I have set delegate for the UITextField and set it to self首先,我为 UITextField 设置了委托并将其设置为 self

self.textField.delegate = self;

Then I know to use shouldChangeCharactersInRange delegate method to detect if a backspace was pressed is all code are in Objective-C.然后我知道使用shouldChangeCharactersInRange委托方法来检测是否按下退格键是所有代码都在 Objective-C 中。 I need in Swift these following method as below is used.我需要在 Swift 中使用以下这些方法。

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    const char * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
    int isBackSpace = strcmp(_char, "\b");

    if (isBackSpace == -8) {
        // NSLog(@"Backspace was pressed");
    }

    return YES;
}

Swift 4.2斯威夫特 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较旧的 Swift 版本

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
}

I prefer subclassing UITextField and overriding deleteBackward() because that is much more reliable than the hack of using shouldChangeCharactersInRange :我更喜欢deleteBackward() UITextField并覆盖deleteBackward()因为这比使用shouldChangeCharactersInRange的黑客更可靠:

class MyTextField: UITextField {
    override public func deleteBackward() {
        if text == "" {
             // do something when backspace is tapped/entered in an empty text field
        }
        // do something for every backspace
        super.deleteBackward()
    }
}

The shouldChangeCharactersInRange hack combined with an invisible character that is placed in the text field has several disadvantages: shouldChangeCharactersInRange hack 结合放置在文本字段中的不可见字符有几个缺点:

  • with a keyboard attached, one can place the cursor before the invisible character and the backspace isn't detected anymore,连接键盘后,可以将光标放在不可见字符之前,不再检测到退格,
  • the user can even select that invisible character (using Shift Arrow on a keyboard or even by tapping on the caret) and will be confused about that weird character,用户甚至可以选择那个不可见的字符(使用键盘上的Shift Arrow ,甚至通过点击插入符号),并且会对那个奇怪的字符感到困惑,
  • the autocomplete bar offers weird choices as long as there's only this invisible character,只要只有这个不可见的字符,自动完成栏就会提供奇怪的选择,
  • Asian language keyboards that have candidate options based on the text field's text will be confused,具有基于文本字段文本的候选选项的亚洲语言键盘将被混淆,
  • the placeholder isn't shown anymore,不再显示placeholder
  • the clear button is displayed even when it shouldn't for clearButtonMode = .whileEditing .即使不应该为clearButtonMode = .whileEditing显示清除按钮。

Of course, overriding deleteBackward() is a bit inconvenient due to the need of subclassing.当然,由于需要子类化,覆盖deleteBackward()有点不方便。 But the better UX makes it worth the effort!但更好的 UX 让它值得付出努力!

And if subclassing is a no-go, eg when using UISearchBar with its embedded UITextField , method swizzling should be fine, too.如果子类化是UISearchBar ,例如,当使用UISearchBar及其嵌入的UITextField ,方法混合也应该没问题。

Swift 5.3斯威夫特 5.3

In some version its changed and now it says:在某些版本中它发生了变化,现在它说:

When the user deletes one or more characters, the replacement string is empty.当用户删除一个或多个字符时,替换字符串为空。

So answer for this:所以回答这个:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
  if string.isEmpty {
    // do something
  }
  return true
}

If you want to detect that some characters will be deleted如果要检测某些字符会被删除

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
  if range.length > 0 {
    // We convert string to NSString instead of NSRange to Range<Int>
    // because NSRange and NSString not counts emoji as one character
    let replacedCharacters = (string as NSString).substring(with: range)
  }
  return true
}

If you want detect backspaces even on empty textField如果你想在空的 textField 上检测退格

class TextField: UITextField {
  var backspaceCalled: (()->())?
  override func deleteBackward() {
    super.deleteBackward()
    backspaceCalled?()
  }
}

Old answer旧答案

Please don't trash your code .请不要丢弃您的代码 Just put this extension somewhere in your code.只需将此扩展名放在代码中的某个位置即可。

extension String {
  var isBackspace: Bool {
    let char = self.cString(using: String.Encoding.utf8)!
    return strcmp(char, "\\b") == -92
  }
}

And then just use it in your functions然后在你的函数中使用它

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
  if string.isBackspace {
    // do something
  }
  return true
}

In Swift 3在斯威夫特 3

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let  char = string.cString(using: String.Encoding.utf8)!
    let isBackSpace = strcmp(char, "\\b")

    if (isBackSpace == -92) {
         print("Backspace was pressed")
    }
    return true
}

:) :)

Try this尝试这个

public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

if(string == "") {

        print("Backspace pressed");
        return true;
    }
}

Note: You can return " true " if you want to allow backspace.注意:如果你想允许退格,你可以返回“ true ”。 Else you can return " false ".否则你可以返回“ false ”。

If u need detect backspace even in empty textField (for example in case if u need auto switch back to prev textField on backSpace pressing), u can use combination of proposed methods - add invisible sign and use standard delegate method textField:shouldChangeCharactersInRange:replacementString: like follow如果您需要在空的 textField 中检测退格(例如,如果您需要在按下退格键时自动切换回上一个 textField),您可以使用建议的方法组合 - 添加不可见符号并使用标准委托方法textField:shouldChangeCharactersInRange:replacementString:喜欢关注

  1. Create invisible sign创建隐形标志

    private struct Constants { static let InvisibleSign = "\\u{200B}" }
  2. Set delegate for textField为 textField 设置委托

    textField.delegate = self
  3. On event EditingChanged check text and if needed add invisible symbol like follow:在事件EditingChanged检查文本,如果需要添加不可见的符号,如下所示:

     @IBAction func didChangeEditingInTextField(sender: UITextField) { if var text = sender.text { if text.characters.count == 1 && text != Constants.InvisibleSign { text = Constants.InvisibleSign.stringByAppendingString(text) sender.text = text } } }

在此处输入图片说明

  1. Add implementation of delegate method textField:shouldChangeCharactersInRange:replacementString:添加委托方法textField:shouldChangeCharactersInRange:replacementString:

     extension UIViewController : UITextFieldDelegate { // MARK: - UITextFieldDelegate func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { let char = string.cStringUsingEncoding(NSUTF8StringEncoding)! let isBackSpace = strcmp(char, "\\\\b") if (isBackSpace == -92) { if var string = textField.text { string = string.stringByReplacingOccurrencesOfString(Constants.InvisibleSign, withString: "") if string.characters.count == 1 { //last visible character, if needed u can skip replacement and detect once even in empty text field //for example u can switch to prev textField //do stuff here } } } return true } }

I implemented this feature:我实现了这个功能:

在此处输入图片说明

And in the case where the last textFiled is empty, I just want to switch to the previous textFiled.而在最后一个 textFiled 为空的情况下,我只想切换到上一个 textFiled。 I tried all of the answers above, but no one works fine in my situation.我尝试了上面的所有答案,但没有一个在我的情况下工作正常。 For some reason, if I add more logic than print in isBackSpace == -92 parentheses block this method just stopped work...出于某种原因,如果我在isBackSpace == -92括号中添加比print更多的逻辑,则此方法只会停止工作...

As for me the method below more elegant and works like a charm:至于我,下面的方法更优雅,而且效果很好:

Swift迅速

class YourTextField: UITextField {

    // MARK: Life cycle

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    // MARK: Methods

    override func deleteBackward() {
        super.deleteBackward()

        print("deleteBackward")
    }

}

Thanks @LombaX for the answer感谢@LombaX 的回答

Swift 4斯威夫特 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

            let  char = string.cString(using: String.Encoding.utf8)!
            let isBackSpace = strcmp(char, "\\b")

            if isBackSpace == -92 {
                print("Backspace was pressed")
                return false
            }
}

Swift 4斯威夫特 4

I find the comparison using strcmp irrelevant.我发现使用strcmp的比较无关紧要。 We don't even know how strcmp is operating behind the hoods.In all the other answers when comparing current char and \\b results are -8 in objective-C and -92 in Swift.我们甚至不知道strcmp在幕后是如何运作的。在比较当前 char 和\\b所有其他答案中,objective-C 中的结果为-8 ,Swift 中的结果为-92 I wrote this answer because the above solutions did not work for me.我写这个答案是因为上述解决方案对我不起作用。 ( Xcode Version 9.3 (9E145) using Swift 4.1 ) (使用 Swift 4.1 Xcode 版本9.3 (9E145)

FYI : Every character that you actually type is an array of 1 or more elements in utf8 Encoding .仅供参考:您实际键入的每个字符都是utf8 Encoding1或多个元素的数组。 backSpace Character is [0] .退格字符是[0] You can try this out.你可以试试这个。

PS : Don't forget to assign the proper delegates to your textFields . PS:不要忘记为您的textFields分配适当的delegates

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let  char = string.cString(using: String.Encoding.utf8)!
    if (char.elementsEqual([0])) {
        print("Backspace was pressed")
    }
    else {
        print("WHAT DOES THE FOX SAY ?\n")
        print(char)
    }
    return true
}

Swift 4斯威夫特 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    //MARK:- If Delete button click
    let  char = string.cString(using: String.Encoding.utf8)!
    let isBackSpace = strcmp(char, "\\b")

    if (isBackSpace == -92) {
        print("Backspace was pressed")

        return true
    }
}

Swift 4: If the user presses the backspace button, string is empty so this approach forces textField to only accept characters from a specified character set (in this case utf8 characters) and backspaces (string.isEmpty case). Swift 4:如果用户按下退格按钮,则字符串为空,因此此方法强制 textField 仅接受来自指定字符集(在本例中为 utf8 字符)和退格(string.isEmpty 情况)的字符。

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if string.cString(using: String.Encoding.utf8) != nil {
        return true
    } else if string.isEmpty {
        return true
    } else {
        return false
    }
}

Swift 5: Per the text view delegate documentation , if the replacement text returned by the shouldChangeTextIn method is empty, the user pressed the backspace button. Swift 5:根据文本视图委托文档,如果 shouldChangeTextIn 方法返回的替换文本为空,则用户按下退格按钮。 If the range upper bound is greater than the range lower bound (or range count is 1 or more), text should be deleted.如果范围上限大于范围下限(或范围计数为 1 或更多),则应删除文本。 If the range upper and lower bounds are the same (or both equal 0), then there is no text to delete (a range of length 0 to be replaced by nothing).如果范围上限和下限相同(或都等于 0),则没有要删除的文本(长度为 0 的范围将被替换为空)。 I tested, and this will get called even on an empty textfield.我测试过,即使在空文本字段上也会调用它。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    
    guard text.isEmpty else { return true } // No backspace pressed
    if (range.upperBound > range.lowerBound) {
        print("Backspace pressed")
    } else if (range.upperBound == range.lowerBound) {
        print("Backspace pressed but no text to delete")
        if (textView.text.isEmpty) || (textView.text == nil) {
            print("Text view is empty")
        }
    }
    return true
}

I came here looking for an answer of how to detect deletions.我来这里是为了寻找如何检测删除的答案。 I wanted to know when the UITextView was empty after a user taps delete .我想知道用户点击后 UITextView 何时为空delete As I was testing, I realized I needed to capture whole word deletions and cuts as well.在测试时,我意识到我还需要捕获整个单词的删除和删减。 Here's the answer I came up with.这是我想出的答案。

extension SomeViewController: UITextViewDelegate {
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        
        let deleteTapped = text == ""
        let cursorAtBeginning = range.location == 0
        let deletedCharactersEqualToTextViewCount = range.length == textView.text.count

        let everythingWasDeleted = deleteTapped && cursorAtBeginning && deletedCharactersEqualToTextViewCount
        if everythingWasDeleted {
            // Handle newly empty view from deletion
        } else {
            // Handle other situation
        }
            
        return true
    }
}

Thanks for all the previous helpful answers.感谢之前所有有用的答案。 I hope this helps someone.我希望这可以帮助别人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM