Search code examples
iosswiftuitextfieldcopy-pasteuitextfielddelegate

Why does returning false in shouldChangeCharactersIn override old text in UITextField?


Problem Code

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

TextField Creation

@discardableResult fileprivate func otpTextField() -> BJOTPTextField {

    let textField = BJOTPTextField()
    textField.text = "1"
    textField.delegate = self
    textField.textColor = .black
    textField.textAlignment = .center
    textField.isSecureTextEntry = true
    textField.keyboardType = .numberPad

    return textField
}

Question

I have a UITextField subclass in my view controller which has text ("1") at the time of initialising. I copy-paste a large text onto the textfield. In shouldChangeCharactersIn method, even though I return explicitly false, the current text ("1") in the textfield gets removed, and becomes empty, why?

shouldChangeCharactersIn documentation

Returns true if the specified text range should be replaced; otherwise, false to keep the old text.

Why is my old text not maintained? And an important observation is that the textfield override happens before shouldChangeCharactersIn is called. Very weird.


PS: The text of the textfield is not altered/assigned anywhere outside of shouldChangeCharactersIn method.


Solution

  • I got why this is happening. The reason is due to the textfield being a SecureTextEntry one.

    The default behaviour in iOS for a password textfield is that, when you have already typed something in the (password) textfield, if it again becomes the first responder (by resigning its first responder status earlier), and you paste or start typing something, the existing contents will be cleared. This happens to allow the user to type in the correct password assuming that the previous entry was incorrect, and since we cannot see the text entered, everything gets erased to allow typing from the beginning.

    This serves to be a good user experience, but this overriding/emptying is not reflected/broadcast anywhere in the shouldChangeCharactersIn delegate method. In fact, the delegate method gets called only after the text overriding happens which actually sounds like a BUG.

    There is no problem and everything works fine as expected if it is not a secure textfield.