UITextField format in xx-xx-xxx
Solution 1
Try below it will work
Objective-C
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
int groupingSize = 2;
if([string length] == 0) {
groupingSize = 4;
}
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init] ;
NSString *separator = @"-";
[formatter setGroupingSeparator:separator];
[formatter setGroupingSize:groupingSize];
[formatter setUsesGroupingSeparator:YES];
[formatter setSecondaryGroupingSize:2];
if (![string isEqual: @""] && (textField.text != nil && textField.text.length > 0)) {
NSString *num = textField.text;
num = [num stringByReplacingOccurrencesOfString:separator withString:@""];
NSString *str = [formatter stringFromNumber:[NSNumber numberWithDouble:[num doubleValue]]];
textField.text = str;
}
return YES;
}
Swift-3
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var groupSize = 2
let separator = "-"
if string.characters.count == 0 {
groupSize = 4
}
let formatter = NumberFormatter()
formatter.groupingSeparator = separator
formatter.groupingSize = groupSize
formatter.usesGroupingSeparator = true
formatter.secondaryGroupingSize = 2
if var number = textField.text, string != "" {
number = number.replacingOccurrences(of: separator, with: "")
if let doubleVal = Double(number) {
let requiredString = formatter.string(from: NSNumber.init(value: doubleVal))
textField.text = requiredString
}
}
return true
}
}
Solution 2
Had a need to do this nicely for a phone numbers with a variable format, here's what I wrote. Feel free to reuse - First I've got a method to filter a formatted string, with # being a number, and any other character being some kind of "filler" that should be conveniently inserted after the user gets to the place where it's needed.
NSMutableString *filteredPhoneStringFromStringWithFilter(NSString *string, NSString *filter)
{
NSUInteger onOriginal = 0, onFilter = 0, onOutput = 0;
char outputString[([filter length])];
BOOL done = NO;
while(onFilter < [filter length] && !done)
{
char filterChar = [filter characterAtIndex:onFilter];
char originalChar = onOriginal >= string.length ? '\0' : [string characterAtIndex:onOriginal];
switch (filterChar) {
case '#':
if(originalChar=='\0')
{
// We have no more input numbers for the filter. We're done.
done = YES;
break;
}
if(isdigit(originalChar))
{
outputString[onOutput] = originalChar;
onOriginal++;
onFilter++;
onOutput++;
}
else
{
onOriginal++;
}
break;
default:
// Any other character will automatically be inserted for the user as they type (spaces, - etc..) or deleted as they delete if there are more numbers to come.
outputString[onOutput] = filterChar;
onOutput++;
onFilter++;
if(originalChar == filterChar)
onOriginal++;
break;
}
}
outputString[onOutput] = '\0'; // Cap the output string
return [NSString stringWithUTF8String:outputString];
}
Now so that they can delete through filler, I modified my should change characters in range.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *filter = @"(###) ### - ####";
if(!filter) return YES; // No filter provided, allow anything
NSString *changedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(range.length == 1 && // Only do for single deletes
string.length < range.length &&
[[textField.text substringWithRange:range] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"0123456789"]].location == NSNotFound)
{
// Something was deleted. Delete past the previous number
NSInteger location = changedString.length-1;
if(location > 0)
{
for(; location > 0; location--)
{
if(isdigit([changedString characterAtIndex:location]))
{
break;
}
}
changedString = [changedString substringToIndex:location];
}
}
textField.text = filteredPhoneStringFromStringWithFilter(changedString, filter);
return NO;
}
This provides a really clean way to force users to enter numbers in a particular format.
Solution 3
Implement your logic inside textField:shouldChangeCharactersInRange:replacementString:
which is a delegate method.
Solution 4
My solution works like that:
Implement in your textfield delegates:
func textFieldDidBeginEditing(_ textField: UITextField) {
// When you start editing check if there is nothing, in that case add the entire mask
if let text = textField.text, text == "" || text == "DD/MM/YYYY" {
textField.text = "DD/MM/YYYY"
textField.textColor = .lightGray
textField.setCursor(position: text.count)
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard var number = textField.text else {
return true
}
// If user try to delete, remove the char manually
if string == "" {
number.remove(at: number.index(number.startIndex, offsetBy: range.location))
}
// Remove all mask characters
number = number.replacingOccurrences(of: "/", with: "")
number = number.replacingOccurrences(of: "D", with: "")
number = number.replacingOccurrences(of: "M", with: "")
number = number.replacingOccurrences(of: "Y", with: "")
// Set the position of the cursor
var cursorPosition = number.count+1
if string == "" {
//if it's delete, just take the position given by the delegate
cursorPosition = range.location
} else {
// If not, take into account the slash
if cursorPosition > 2 && cursorPosition < 5 {
cursorPosition += 1
} else if cursorPosition > 4 {
cursorPosition += 2
}
}
// Stop editing if we have rich the max numbers
if number.count == 8 { return false }
// Readd all mask char
number += string
while number.count < 8 {
if number.count < 2 {
number += "D"
} else if number.count < 4 {
number += "M"
} else {
number += "Y"
}
}
number.insert("/", at: number.index(number.startIndex, offsetBy: 4))
number.insert("/", at: number.index(number.startIndex, offsetBy: 2))
// Some styling
let enteredTextAttribute = [NSForegroundColorAttributeName: UIColor.black, NSFontAttributeName: UIFont.systemFont(ofSize: 15)]
let maskTextAttribute = [NSForegroundColorAttributeName: UIColor.lightGray, NSFontAttributeName: UIFont.systemFont(ofSize: 15)]
let partOne = NSMutableAttributedString(string: String(number.prefix(cursorPosition)), attributes: enteredTextAttribute)
let partTwo = NSMutableAttributedString(string: String(number.suffix(number.count-cursorPosition)), attributes: maskTextAttribute)
let combination = NSMutableAttributedString()
combination.append(partOne)
combination.append(partTwo)
textField.attributedText = combination
textField.setCursor(position: cursorPosition)
return false
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let text = textField.text, text != "" && text != "DD/MM/YYYY" {
// Do something with your value
} else {
textField.text = ""
}
}
And that little helper as an extension:
extension UITextField {
func setCursor(position: Int) {
let position = self.position(from: beginningOfDocument, offset: position)!
selectedTextRange = textRange(from: position, to: position)
}
}
PS: there is still a bug in that implementation when you try to move the cursor while editing
Solution 5
Here is my take on it after looking at everyone's answers.
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let separator = "-"
let filler = "X"
if var number = textField.text, string != "" {
number = number.replacingOccurrences(of: separator, with: "")
number = number.replacingOccurrences(of: filler, with: "")
if number.count == 10 { return false }
number += string
while number.count < 10 { number += "X" }
number.insert("-", at: number.index(number.startIndex,
offsetBy: 6))
number.insert("-", at: number.index(number.startIndex,
offsetBy: 3))
textField.text = number
}
return false
}
PJR
Check my Sample codes on Github: https://github.com/paritsohraval100 Cocoa Controls https://www.cocoacontrols.com/controls/pjr-scrollview-slider https://www.cocoacontrols.com/controls/pjr-nsstring-category https://www.cocoacontrols.com/controls/pjrsignaturedemo https://www.cocoacontrols.com/controls/pulseanimation Please check My Blog: http://iosdevblogs.blogspot.in/
Updated on July 09, 2022Comments
-
PJR almost 2 years
I am using UITextField and i want that should take character in the format of xx-xx-xxx only numbers.
any help ?
-
Narayana over 12 yearsremove auto release at NSNumberFormatter init
-
Peter almost 11 yearsI don't know why this hasn't gotten more up-votes. It did exactly what I needed it to do with an absolute minimum of fuss: I copied, pasted, changed the very clear format mask (different format needed) and it worked flawlessly. Perfect.
-
Chris Klingler over 10 yearsTo make this idea work for dates and times (including am/pm). 1. NSString *filter = @"##/##/#### ##:## ##"; 2. Char set - [NSCharacterSet characterSetWithCharactersInString:@"0123456789APMapm"], 3. Add else to filteredPhoneStringFromStringWithFilter type method - else if((originalChar == 'a') || (originalChar == 'p') || (originalChar == 'm') || (originalChar == 'A') || (originalChar == 'P') || (originalChar == 'M')){ outputString[onOutput] = originalChar; onOriginal++; onFilter++; onOutput++; }
-
Andre Cytryn about 9 yearshow to fit a filter like this: (##) ####-####. When I try this, the first parenthesis is never deleted.
-
BadPirate about 9 years@AndréCytryn - Thats because this was designed to enforce the format... If the first few characters aren't variable (not #) then they will always be present (can't be deleted) -- Though you can just modify the methods above to suit your need :)
-
Frederic Adda over 8 yearsYou should explain a bit more what your code does, it is really hard to understand ... don't forget that the one reading this hasn't created your code : what may seem obvious to you is not to anyone reading it. Thanks !
-
Inuyasha about 8 years@Narayana it' working but if my string is leading by 0, it will remove 0 automatically. What should I do to prevent it from remove 0 automatically?
-
Abhishek Thapliyal almost 7 yearsCan you add swift version also as 2 -3 changes are required in swift to avoid crash
-
Narayana almost 7 years@AbhishekThapliyal please check updated answer i have added swift3 code, Still if you face any issue let me know
-
Abhishek Thapliyal almost 7 years@Narayana : If i need to use '\' in place of '-' how i can do that also set character limit?
-
Narayana almost 7 years@AbhishekThapliyal please check above updated answer let separator = "-" in place of - use \\ it will work as you required
-
Abhishek Thapliyal almost 7 years@Narayana: Thanks alot!!! one last help how can i make format: xx-xx-xxxx i.e dd-mm-yyyy
-
Tamil about 6 yearsthis works fine but i am not able to delete a number or place cursor inbetween and change a number
-
karthikeyan about 5 yearsDid you complete this without bug? If yes share demo project Thanks in advance
-
Fast over 4 yearsdoesn't work correctly when i try to delete the text from textfield
-
Dilip Tiwari about 4 yearsHow to enter 9 digits in textfield like XXX-XXXXXX in swift
-
Alirza Eram over 2 yearsyou have to use
string.count
instead ofstring.characters.count
for Swift 4 or later!