The range passed to shouldChangeCharacters
refers to a range of the text field's text, before the change. When you normally insert text using the keyboard, the range has a length of 0, and replacementString
is the character that you are trying to insert. The idea is that you are replacing an empty range of the text, with the new character, hence "inserting" that character.
As a result, doing a replacement of that same range with " "
to the placeholder string, will insert a space into the placeholder string.
Rather than using the given range on the placeholder string, you should use it on the text field's text. That is what the range means anyway.
You can compute the new text of the text field, after the insertion, by doing:
let newText = (textField.text! as NSString).replacingCharacters(in: range, with: string)
Then, replace the first newText.count
characters of the placeholder string with spaces. Set this as the new text of the placeholder label.
// note that I changed this to a let, because I do not change the placeholder string
let placeHolderLabelString = "___ ___ ___"
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newText = (textField.text! as NSString).replacingCharacters(in: range, with: string)
// do not allow more text than the placeholder
if newText.count > placeHolderLabelString.count {
return false
}
let startIndex = placeHolderLabelString.startIndex
let replaceableRange = startIndex..<placeHolderLabelString.index(startIndex, offsetBy: newText.count)
let newPlaceholder = placeHolderLabelString.replacingCharacters(
in: replaceableRange, with: String(repeating: " ", count: newText.count)
)
placeholderLabel.text = newPlaceholder
return true
}
From the looks of it, you also want to automatically add spaces in some places.
To do that, you need to set textField.text
:
// after the newText.count > placeHolderLabelString.count check
let spacesRemoved = newText.replacingOccurrences(of: " ", with: "")
textField.text = formatPhoneNumber(spacesRemoved)
where formatPhoneNumber
is a method that inserts spaces appropriately. Its implementation is left as an exercise for the reader.
Then, the rest of the code should replace the first textField.text!.count
characters of placeHolderLabelString
, rather than newText.count
. Because newText
is now no longer the text in the text field.
let startIndex = placeHolderLabelString.startIndex
// note that I'm using textField.text instead of newText here,
// because *that* is actually the text of the text field now
let replaceableRange = startIndex..<placeHolderLabelString.index(startIndex, offsetBy: textField.text!.count)
let newPlaceholder = placeHolderLabelString.replacingCharacters(
in: replaceableRange, with: String(repeating: " ", count: newText.count)
)
placeholderLabel.text = newPlaceholder
// the text field's text should not change, because we set it programmatically to the desired value already.
return false