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
}

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.


Swift 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

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

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