Detect backspace Event in UITextField

橙三吉。 提交于 2019-11-28 04:32:41
Long Pham

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
}

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

:)

I prefer subclassing UITextField and overriding deleteBackward() because that is much more reliable than the hack of using 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:

  • 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,
  • 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,
  • the clear button is displayed even when it shouldn't for clearButtonMode = .whileEditing.

Of course, overriding deleteBackward() is a bit inconvenient due to the need of subclassing. But the better UX makes it worth the effort!

And if subclassing is a no-go, e.g. when using UISearchBar with its embedded UITextField, method swizzling should be fine, too.

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

  1. Create invisible sign

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

    textField.delegate = self
    
  3. On event EditingChanged check text and if needed add invisible symbol like follow:

    @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:

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

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. Else you can return "false".

Amr Angry

Swift 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

I find the comparison using strcmp irrelevant. 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. I wrote this answer because the above solutions did not work for me. ( Xcode Version 9.3 (9E145) using Swift 4.1 )

FYI : Every character that you actually type is an array of 1 or more elements in utf8 Encoding. backSpace Character is [0]. You can try this out.

PS : Don't forget to assign the proper delegates to your textFields.

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

Please don't trash your code. Just put this extension somewhere in your code. (Swift 4.1)

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
}

I implemented this feature:

And in the case where the last textFiled is empty, I just want to switch to the previous 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...

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

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

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 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
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!