I ran into this same issue today and was sort of astounded that there is no way to detect that change from any notification or delegate method as I think this is a fairly common need. I am building a multi field text box input for capturing a pin code. I am auto-advancing to next / previous fields upon entering a value and don't want the keyboard to switch back to default each time I switch fields. I came up with this solution (note: This doesn't solve it perfectly because it requires the user to type something, but solves it for similar needs to this pin view).
I have a custom view called MyPinView, this view has var
var keyBoardType = UIKeyboardType.alphabet
and an array of UITextFields
var pinFields = [UITextField]()
that is laid out like so.
For a simplified version, I won't go through all the details of handling edge cases that require shouldChangeCharactersInRange
to be implemented and will stick with my event handler implementation.
Basically, you need to see what text was typed and determine what keyboard to show when advancing fields (mine only supports numbers and letters switched with the abc / 123 button so I will ignore emoji and other types).
// MARK: UITextField Event
func textFieldDidChange(textField: UITextField) {
// If the user typed one character, move to the next cell.
if (textField.text?.count == 1) {
let index = pinFields.index(of: textField)
textField.resignFirstResponder()
if (pinFields.count > index! + 1) {
let pinField = pinFields[index! + 1]
self.updateKeyboardTypeForString(input: textField.text)
pinField.keyboardType = self.keyBoardType
pinField.becomeFirstResponder()
}
} // If they deleted the character move to previous cell
else if (textField.text?.count == 0) {
let index = pinFields.index(of: textField)
if (index! - 1 >= 0) {
let pinField = pinFields[index! - 1]
self.updateKeyboardTypeForString(input: pinField.text)
pinField.keyboardType = self.keyBoardType
pinField.becomeFirstResponder()
}
}
}
// MARK: - Keyboard Type Helper
func updateKeyboardTypeForString(input:String?) {
let letters = NSCharacterSet.letters
let digits = NSCharacterSet.decimalDigits
let pinText = input == nil ? "" : input
for uniScalar in pinText!.unicodeScalars {
if letters.contains(uniScalar) {
self.keyBoardType = UIKeyboardType.alphabet
} else if digits.contains(uniScalar) {
self.keyBoardType = UIKeyboardType.numbersAndPunctuation
}
}
}
This allows you to track where the keyboard last left off. Obviously if your text fields allow more than one character, you would need to grab only the last types character and pass it to updateKeyboardTypeForString
. The main case where this won't help is in preserving keyboard state between input fields where the user has switched the keyboard, but not typed anything. Overall though, this helps this type of scenario to be more usable.
Hope this helps, happy programming.