I want the user to enter a Social Security Number in the format ••• •• ••••. The user types the first 3 numbers, then I append a space manually. Then they enter 2 more numbers and I manually append a space. Of course, even the spaces are being displayed as •. Is there a native way to change this behavior? I am currently using a funky manual implementation of this.
What if instead of spaces, you use three different text entries? Then when each user enters the first three characters, you jump to the second text entry? When he types two more, you jump to the third text entry.
Here is an example:
EDIT: Now supports backspace (Thanks to @triple-s).
extension ViewController: UITextFieldDelegate {
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
switch textField {
// Jump forwards
case self.textField1 where string.characters.count == 3 :
self.textField2.becomeFirstResponder()
case self.textField2 where string.characters.count == 2 :
self.textField3.becomeFirstResponder()
// Jump backwards
case self.textField3 where string.characters.count == 0 :
self.textField2.becomeFirstResponder()
case self.textField2 where string.characters.count == 0 :
self.textField1.becomeFirstResponder()
default :
break
}
return true
}
}
firstResponser
–
Underclothes string
is just the new input: it will have length 1. You need to check the location of the proposed range. The second part is pretty much just wrong: textField:shouldChangeCharactersInRange:replacementString:
isn't called for a backspace at the beginning of a text field. –
Tow This can be achieve in one single textField as asked. I only tapped "1", in the gif.
You select your keypad type to be number (0-9), which can ensure everything that will be input there is number only.
Then you can adopt the textField delegate and implement the delegate method
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { print(string)
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)! let isBackSpace = { return strcmp(char, "\\b") == -92} if (textField.text?.characters.count == 3 && !isBackSpace()) || (textField.text?.characters.count == 6 && !isBackSpace()){ textField.text = textField.text! + " " } if (textField.text?.characters.count) == 11 && !isBackSpace(){ ssnString = textField.text! self.view.endEditing(true) } return true
}
This includes the logic of adding space after third digit if you are not backspacing and same for the after 6th digit.
Also, after user input 11 digit, it will not allow user to input more number as the format of SSN, after 11 digit is input, the SSN is saved in ssnString, which will be used for you special masking.
- Because you don't want to mask space, we can not use secureTextEntry. So in the didEndEditing, I gave an condition only if the user enter the full SSN, we will mask it, which can be modified to any scenario if you want. But i think this makes more sense.
func textFieldDidEndEditing(textField: UITextField) {
if textField.text?.characters.count == 11 {
maskSSNTextField()
}
}
In the maskSSNTextField method,
func maskSSNTextField() {
textField.text = "••• •• ••••"
}
- Finally, we need to unmask it when user come back to it, if they want to change the text
func textFieldDidBeginEditing(textField: UITextField) {
if textField.text == "••• •• ••••"{
textField.text = ssnString
}
}
This fully fulfilled your requirement. Please let me know if you have other question.
I changed the didChangeInRange method to meet your new requirement, although I think my previous answer could work. Now it works as in the gif. If you want it to be still masked, you can change the code in textField did begin editing.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = { return strcmp(char, "\\b") == -92}
if (textField.text?.characters.count == 3 && !isBackSpace()) || (textField.text?.characters.count == 6 && !isBackSpace()){
textField.text = textField.text! + " "
ssnString = ssnString + " "
}
if isBackSpace() {
ssnString = ssnString.substringToIndex(ssnString.endIndex.predecessor())
}else {
ssnString = ssnString + string
print(ssnString)
if ssnString.characters.count >= 2 {
var starString = ""
for i in 0...ssnString.characters.count-2 {
if i==3 || i==6 {
starString = starString+" "
}else {
starString = starString+"•"
}
}
textField.text = ""
print(ssnString.characters.last)
textField.text = starString
}
}
if (textField.text?.characters.count) == 11 && !isBackSpace(){
self.view.endEditing(true)
}
return true
}
The simple solution I have been using is to convert my input string to an NSAttributedString with text spacing (.kern) attributes added at the proper locations and keeping isSecureTextEntry set to true. Disabling isSecureTextEntry and doing it by hand in addition of being overly complex could have security implications at least if someone is using a third party keyboard.
var ssnText = "123456789"
let spacerPositions = [ 2, 4 ]
let spacingAmount: CGFloat = 5.0
let spacerRanges:[NSRange] = spacerPositions
.filter { $0 < ssnText.count - 1 }
.map { NSRange(location: $0, length: 1) }
let attributedString = NSMutableAttributedString(string: ssnText)
for range in spacerRanges {
attributedString.addAttribute(.kern, value: spacingAmount, range: range)
}
textField.attributedText = attributedString
calling that stuff in textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String)
.
© 2022 - 2024 — McMap. All rights reserved.