Getting an IndexOutofBoundsException when implementing delegate method shouldChangeCharactersInRange in iOS - ios

I have a UITextField in my application that I want to function like a calculator. I need it to have the default value of 0.00, and as the user enters digits, the numbers move from right to left, replacing the zeroes in 0.00 one digit at a time, and the application should be smart enough to add commas (,) after every three digits. To do this, I am implementing the delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {}
as follows:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if([[string stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]]
isEqualToString:#""])
return YES;
NSString *previousValue = [[[textField.text stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] stringByReplacingOccurrencesOfString:#"." withString:#""] stringByReplacingOccurrencesOfString:#"," withString:#""];
string = [string stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]];
NSString *modifiedValue = [NSString stringWithFormat:#"%#%#",previousValue,string];
modifiedValue = [NSString stringWithFormat:#"%#.%#",[modifiedValue substringToIndex:modifiedValue.length-2],[modifiedValue substringFromIndex:modifiedValue.length-2]];//this is the line that is causing the error
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];
textField.text = modifiedValue;
return NO;
}
However, I am getting the following error:
'NSRangeException', reason: '*** -[__NSCFString substringToIndex:]: Range or index out of bounds'
This error was thrown the moment I tried to enter a number in the textfield. Can anyone see what I am doing wrong?
Thanks in advance to all who reply.

Yeah, this bit right here:
[modifiedValue substringToIndex:modifiedValue.length-2]
seems to assume that the length of the string is at least 2.
Which is probably isn't, at least when the user is typing his/her first character into an empty text field.
How about putting a length check around the whole thing, only doing the guts of the "shouldChangeCharactersInRange" method if the length is greater than two characters?

Related

How to add commas automatically for every 3 numbers while user input the text field - iOS Objective C [duplicate]

This question already has answers here:
How do you dynamically format a number to have commas in a UITextField entry?
(9 answers)
Closed 5 years ago.
How to add commas for every 3 numbers while user inputting the text field and also display the number with commas on the label?
I am using this function but it does not work.
- (void)commaFormatter
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setGroupingSeparator:#","];
[numberFormatter setGroupingSize:3];
[numberFormatter setDecimalSeparator:#"."];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
}
If you want a number like 123,456,789,5 then use shouldChangeCharactersInRange delegate method and write below code inside it.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSString *strTemp = textField.text;
strTemp = [textField.text stringByReplacingOccurrencesOfString:#"," withString:#""];
if (strTemp.length%3==0&&![string isEqualToString:#""]&&textField.text.length!=0&&![[textField.text substringWithRange:NSMakeRange(textField.text.length-1, 1)] isEqualToString:#","]) {
_txtField.text = [_txtField.text stringByAppendingString:#","];
}
return YES;
}

Check if an NSString is a double [duplicate]

This question already has an answer here:
parsing NSString to Double
(1 answer)
Closed 7 years ago.
How do I check if a UITextField's text is a double or not?
Simple validation required, if the entered textfield value is a valid double value or not?
Valid double values: 1.00, 1.01 or 1.00001
Invalid double values: .0.1, .001.11, 1.0.1 or 1...0 etc.
mytxtfield.text=#"12.00"; //ok
mytxtfield.text=#"1.2..0"; // is invalid and so on
EDIT: My answer that works thanx sahzad ali
//------------
-(BOOL)isvalidDouble:(NSString*)txtstring
{
NSArray *dotSeparratedArray = [txtstring componentsSeparatedByString:#"."];
NSInteger count=[dotSeparratedArray count];
count=count-1;
if(count >1)
{
return TRUE ; //invalid
}
return FALSE; //valid
}
You can limit user from entering multiple dots or you can use same logic in your TextFieldDidEndEditing method. If componentsSeparatedByString returns greater than 2 it means there are multiple dots.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *myString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSArray *dotSeparratedArray = [myString componentsSeparatedByString:#"."];
if([dotSeparratedArray count] >= 2)
{
NSString *string1=[NSString stringWithFormat:#"%#",[dotSeparratedArray objectAtIndex:1]];
return !([string1 length]>1);
}
return YES;
}
I don't know whether or not this will serve your purposes, but you could use NSNumberFormatter and customize it to your needs:
NSNumberFormatter *myFormatter = [[NSNumberFormatter alloc] init];
[myFormatter setFormatWidth:7];
[myFormatter setPaddingCharacter:#"0"];
[myFormatter setMinimumSignificantDigits:0];
[myFormatter setMinimum:#0];
[myFormatter setMaximum:#9999999];
[myClientIDTextField setFormatter:myFormatter];

add prefix to UITextField

I want to add prefix of UITextfield text. The UITextfield text length less than 7. How many characters are less than 7, that all replace with zeros.
If text is "1234", add prefix like "0001234".
If text is "12345", add prefix like "0012345".
If text is "123", add prefix like "0000123".
can any one suggest me, how to implement.
It sounds like what we actually want is a numbers-only string that is always 7-characters long, with the left-most characters filled in with padded zeros for anything the user has not entered, correct?
So, we need a handful of methods to make this as easy as possible.
First, this one doesn't make sense right now, but we want a method to remove the zeros we padded at the front (it'll make sense later).
So, borrowing from this Stack Overflow answer...
- (NSString *)stringByRemovingPaddedZeros:(NSString *)string {
NSRange range = [string rangeOfString:#"^0*" options:NSRegularExpressionSearch];
return [string stringByReplacingCharactersInRange:range withString:#""];
}
And we'll borrow from Ilesh's answer for adding the padded zeros:
- (NSString *)stringByAddingPaddedZeros:(NSString *)string padLength:(NSInteger)length {
NSString *padding = [#"" stringByPaddingToLength:(length - string.length) withString:#"0" startingAtIndex:0];
return [NSString stringWithFormat:#"%#%#", padding, string];
}
So now we can go back and forth between padded and unpadded strings, right?
So now, one last step, implementing shouldChangeCharactersInRange:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
newString = [self stringByRemovingPaddedZeros:newString];
newString = [self stringByAddedPaddedZeros:newString padLength:7];
textField.text = [newString subStringToIndex:7];
return NO;
}
We always return NO here, as we're setting the textField.text property manually. Now when there are 7 characters (and no leading zeros), the user can type no more. If there are 7 characters and the user hits backspace, they should all shift right one and a zero added to the front. If there are leading zeros at the front, typing characters should shift everything left and drop a leading zero, and add a new character to the front.
As an additional note, this code does not take care of verifying that the user is only entering digits. Some extra logic would be required for that. I'd simply recommend checking that the replacementString (string) is only digits before you get into any of the other code in shouldChangeCharactersInRange here.
Here printing textfield text on button click. Check the code inside the method.
- (IBAction)logTextFieldText:(id)sender
{
NSMutableString *str=[[NSMutableString alloc]init];
if (_txtf.text.length<7)
{
for (int i=0;i<7-_txtf.text.length; i++)
{
[str appendString:#"0"];
}
[str appendString:_txtf.text];
}
NSLog(#"final text is: %#",str);
}
Implement the UITextFieldDelegate method textFieldDidEndEditing: to pad the 0's in.
- (void)textFieldDidEndEditing:(nonnull UITextField *)textField
{
if (textField.text.length < 7) {
// Create a string of 0's to pad with
NSString *padding = [#"" stringByPaddingToLength:(7 - textField.text.length) withString:#"0" startingAtIndex:0];
NSMutableString *change = [textField.text mutableCopy];
// Insert the 0's string
[change insertString:padding atIndex:0];
textField.text = change;
}
}
If you want to fix the length of UITextField text than use this UITextField delegate method.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (self.txtGet.text.length>=7) {
return NO;
}
return YES;
}
and the completion editing (or done button ) you add this line in before using the UITextField value.
NSString *padding = [#"" stringByPaddingToLength:(7 - self.txtGet.text.length) withString:#"0" startingAtIndex:0];
NSMutableString *change = [self.txtGet.text mutableCopy];
// Insert the 0's string
[change insertString:padding atIndex:0];
self.txtGet.text = change;
I think its helpful to you. Thank you.

Numbers separated by commas in UITextField not working

I'm trying to add a decimal after every 3 characters. (Counting backwards like this: 1,325,541 instead of 1325451.)
Here is what I tried:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSNumberFormatter *numberFormat = [[NSNumberFormatter alloc] init];
[numberFormat setGroupingSeparator:#","];
[numberFormat setGroupingSize:3];
[numberFormat setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *amount = [numberFormat numberFromString:textField.text];
textField.text = [numberFormat stringFromNumber:amount];
return YES;
}
It doesn't insert a comma after every 3 characters. What can I do to fix it?
Well, this ends up being a little more nuanced than you might expect, but this is what I came up with:
As #bgfriend0's comment mentions, textField:shouldChangeCharactersInRange:replacementString gets called before applying an edit to a text field (this is useful for other reasons). Meaning, that if your current string was 123 and a user goes to type in 4, the method would be passed the following as parameters:
textField: the textField, but note at this point textField.text is 123
range: NSRange{2, 0}
string: 4
And there is a way to make this function work from within that method, but there's a better way (I think).
After you instantiate your textField, add a target to listen for editing events:
[textField addTarget:self
action:#selector(formatNumberIfNeeded:)
forControlEvents:UIControlEventEditingChanged];
This will get called anytime an editing change happens in a UITextField. The SEL that we'll be performing will look like this:
- (void)formatNumberIfNeeded:(UITextField *)textField{
// you'll need to strip the commas for the formatter to work properly
NSString * currentTextWithoutCommas = [textField.text stringByReplacingOccurrencesOfString:#"," withString:#""];
NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
NSNumber * numberFromString = [numberFormatter numberFromString:currentTextWithoutCommas];
NSString * formattedNumberString = [numberFormatter stringFromNumber:numberFromString];
textField.text = formattedNumberString;
}
Now, things get slightly more tricky if you need to localize, but cross that bridge if needed.
As for textField:shouldChangeCharactersInRange:replacementString, that's a much better place to do character validation. So, a basic validation to check for letters could look like:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSRange illegalCharacterEntered = [string rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]];
if ( illegalCharacterEntered.location != NSNotFound ) {
return NO;
}
return YES;
}
So with those two bits of code, you'll be able to update the textfield string to include a comma every 3rd character, and users wont be able to enter any letters of the alphabet (but they can still input other 'illegal' characters, so extend that validation as needed).

Check the content of NSString with data in NSArray

I have the next problem:
In a UITextField, I need to check if the user inserts an invalid character for the Name, like [ ] { }.
My question is, how can I check the content of the UITextField with one NSArray with the invalid characters inside?
If you want to check single letter input from keyboard on some input politics
you need to use textField delegate method
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
You should check if string contains in your exception array. If so you return NO and symbol doesn't appear on screen.
Ok, I explain what I do to solve the problem. Cause I'm controlling the behavior of a UITextField where I use to the user enter the name and one comment, I make a character set with all characters I allow and I put inside the shouldChangeCharactersInRange Method:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSCharacterSet *invalidCharSet = [[NSCharacterSet characterSetWithCharactersInString:#" ,.abcdefghijklmnopqrstuvwxyzçñ?!'ABCDEFGHIJKNMLOPQRSTUVWXYZÇÑ-_;:0123456789+*()-/#áàéèíìóòúù"] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:invalidCharSet] componentsJoinedByString:#""];
return [string isEqualToString:filtered];
}

Resources