ObjC - Using NSRange, Allow User to Enter Only One decimal point - - iphone

building a simple calculator here to get my feet wet with iOS dev. I want to prohibit the entry of more than one decimal point. Put my logic in the comment.
It builds and runs with no problem, but it's not blocking the entry of more than one "." any help is appreciated. Thanks.
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [[sender titleLabel] text];
if (userIsInTheMiddleofTypingANumber) {
[display setText:[[display text] stringByAppendingString:digit]];
// if the user enters a decimal point, check if the existing display text contains a "." if not, then allow it, but if it does, do not allow it.
NSString *decimal = #".";
NSRange range = [digit rangeOfString:decimal];
if (range.location == NSNotFound) {
[display setText:digit];
}
} else {
[display setText:digit];
userIsInTheMiddleofTypingANumber = YES;
}
}

- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [[sender titleLabel] text];
NSString *decimal = #".";
BOOL decimalAlreadyEntered = [display.text rangeOfString:decimal].location == NSNotFound ? NO : YES;
if (userIsInTheMiddleofTypingANumber) {
if (([digit isEqual:decimal] && !decimalAlreadyEntered) || !([digit isEqual:decimal])) {
[display setText:[[display text] stringByAppendingString:digit]];
}
}
else if (display.text isEqual:#"0" && digit == decimal){
[display setText:[[display text] stringByAppendingString:digit]];
userIsInTheMiddleofTypingANumber = YES;
}
else {
[display setText:digit];
userIsInTheMiddleofTypingANumber = YES;
}
}
I think this should work. It looks like you are appending the digit first thing in your loop, even if a decimal has been entered. I haven't tried this code in the compiler but it checks to see if a decimal has been entered first.

Don't frustrate your user. Give them feedback about what you can and cannot accept for input:
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *char = [[sender titleLabel] text];
if( [char isEqualTo:#"."] ){
[sender setEnabled:NO];
}
[display setText:[[display text] stringByAppendingString:char]];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)replacementRange replacementString:(NSString *)replacementString {
// Re-enable the button if the decimal point is deleted
NSRange rangeForPoint = [[display.text substringWithRange:replacementRange]
rangeOfString:#"."];
if( NSNotFound != rangeForPoint.location ){
// You may also want to check that replacementString does not contain a new
// decimal point, in the case of pasted text, e.g.
[decimalPointButton setEnabled:YES];
}
}
This delegate method, textField:shouldChangeCharactersInRange:replacementString: is really the key to handling restricted input. You should also look at NSFormatter, which can take a format and validate input as the user types, using its isPartialStringValid... methods.

You might want to check an earlier post by me, which deals with sort of the same issue. I tried creating a textfield that accepts limited input (for example only numbers in the following format: 0.00). It never worked completely great (it had some bugs with currency I believe), but it might be good enough for your purposes:
Re-Apply currency formatting to a UITextField on a change event

This worked for me:
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [[sender titleLabel] text];
if (userIsInTheMiddleOfTypingANumber)
{
if (decimalEntered)
{
NSRange range = [digit rangeOfString:#"."];
if (range.location == NSNotFound)
{
[display setText:[[display text] stringByAppendingString:digit]];
}
else
{
NSLog(#"You can't enter more than one decimal");
}
}
else if (!decimalEntered)
{
NSRange range = [digit rangeOfString:#"."];
if (range.location == NSNotFound)
{
[display setText:[[display text] stringByAppendingString:digit]];
}
else
{
[display setText:[[display text] stringByAppendingString:digit]];
decimalEntered = YES;
}
}
}
else
{
decimalEntered = NO;
[display setText:digit];
userIsInTheMiddleOfTypingANumber = YES;
}
}

Try below updated code of yours
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [[sender titleLabel] text];
if (userIsInTheMiddleofTypingANumber)
{
// if the user enters a decimal point, check if the existing display text contains a "." if not, then allow it, but if it does, do not allow it.
NSString *decimal = #".";
NSRange range = [digit rangeOfString:decimal];
if (range.location == NSNotFound)
{
[display setText:[[display text] stringByAppendingString:digit]];
[display setText:digit];
}
}
else
{
[display setText:digit];
userIsInTheMiddleofTypingANumber = YES;
}
}

Related

I'm Trying to figure out the timing of the iOS Keyboard

So here is the situation of the timing. I have a UILabel that I want to update every time the keyboard updates a UITextField. I have two UITextFields but only one is ever the first responder so don't worry about there being two I have them for back end purposes. The problem is the timing from the UILabel updating and the UITextField delegate function
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;˚
The replacement string doesn't get added until YES is returned to by the above function. I need to update my labels either after this function is called or during this function. I can't seem to figure out how it will work. The UILabel is always one character behind. Below is my code in general for this section.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if([self.hiddenTextFieldForTimeMinutes.text length] == 2 && [self.hiddenTextFieldForTime.text length] == 2 && ![string isEqualToString:#""])
{
return NO;
}
[self syncTextFieldsMinutesAndHours: string];
// This returns the default Yes;
return YES;
}
- (void) setAccessoryLabel: (NSString *) hourString minutesString: (NSString *) minuteString
{
timeAccessoryLabel.text = [NSString stringWithFormat:#"%#:%#", hourString, minuteString];
}
- (void) syncTextFieldsMinutesAndHours: (NSString *) string
{
// These are the textFields
NSMutableString *hoursString = [NSMutableString stringWithFormat: #"%#", self.hiddenTextFieldForTime.text];
NSMutableString *minutesString = [NSMutableString stringWithFormat: #"%#", self.hiddenTextFieldForTimeMinutes.text];
if([self.hiddenTextFieldForTimeMinutes.text length] == 2 && ![string isEqualToString: #""])
{
[hoursString appendString: [NSString stringWithFormat:#"%c", [minutesString characterAtIndex:0]]];
[self.hiddenTextFieldForTime setText:[NSString stringWithFormat:#"%#", hoursString]];
[self.hiddenTextFieldForTimeMinutes setText: [self.hiddenTextFieldForTimeMinutes.text substringFromIndex:1]];
} else if([self.hiddenTextFieldForTimeMinutes.text length] == 2 && [string isEqualToString: #""])
{
// Hours has nothing in it
if([hoursString length] == 0)
{
return;
} else if([hoursString length] == 1)
{
// Since the timing of the add and remove of the string is done by return of the delegate we append the string to the beginning first then return.
[self.hiddenTextFieldForTimeMinutes setText: [NSString stringWithFormat:#"%c%#", [self.hiddenTextFieldForTime.text characterAtIndex:0], self.hiddenTextFieldForTimeMinutes.text]];
[self.hiddenTextFieldForTime setText:#""];
} else if ([hoursString length] == 2)
{
// Since the timing of the add and remove of the string is done by return of the delegate we append the string to the beginning first then return.
[self.hiddenTextFieldForTimeMinutes setText: [NSString stringWithFormat:#"%c%#", [self.hiddenTextFieldForTime.text characterAtIndex:1], self.hiddenTextFieldForTimeMinutes.text]];
[self.hiddenTextFieldForTime setText: [NSString stringWithFormat:#"%c", [self.hiddenTextFieldForTime.text characterAtIndex:0]]];
}
}
[self setAccessoryLabel: self.hiddenTextFieldForTime.text minutesString:self.hiddenTextFieldForTimeMinutes.text];
}
yes. The text of the textField in textField:shouldChangeCharactersInRange:replacementString: will still have the old value, because it only gets changed after you answered yes to the question if the text should change.
You have two options.
create the NSString that your textField will have after you returned YES yourself:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if([self.hiddenTextFieldForTimeMinutes.text length] == 2 && [self.hiddenTextFieldForTime.text length] == 2 && ![string isEqualToString:#""])
{
return NO;
}
NSString *realString = [textField.text stringByReplacingCharactersInRange:range withString:string];
[self syncTextFieldsMinutesAndHours: realString];
// This returns the default Yes;
return YES;
}
or add a IBAction that gets called after the editing took place:
- (void)viewDidLoad {
// your viewDidLoad implementation
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
- (IBAction)textFieldDidChange:(UITextField *)sender {
[self syncTextFieldsMinutesAndHours: sender.text];
}
After mulling for a few minutes, this could be a run loop issue. Try adding this before you call the method to update your UILabel:
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
//update label
[self updateLabelWithText:foo andText:bar];
Or try using GCD:
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
dispatch_async(main_queue, ^{
// UI Updates here
[self updateLabel...];
});
});

Log history of entered numbers in a simple calculator

I'm working on an update for a simple calculator app. The update includes a history display. My problem is that when I'm typing a number, the Log Display is ignoring the first number but logs perfectly all the rest. "Example", if I type 1234567 + 1234 ... the Log displays "234567 + 234". So the first number is never there when it should be.
- (IBAction)digitPressed:(UIButton*)sender;
{
NSString *digit = [[sender titleLabel] text];
NSRange range = [[display text] rangeOfString:#"."];
if (userIsInTheMiddleOfTypingANumber) {
if ( ! ([digit isEqual:#"."] && (range.location != NSNotFound)))
[display setText:[[display text]stringByAppendingString:digit]];
self.logDisplay.text = [self.logDisplay.text stringByAppendingFormat:#"%#", digit];
} else {
if ([digit isEqual:#"."]) {
[display setText: #"0."];
}
else {
[display setText:digit];
}
userIsInTheMiddleOfTypingANumber = YES;
}
}
Thanks in advance!
The reason you're loosing first digit is that if (userIsInTheMiddleOfTypingANumber) { returns false for the first time.
For the first press your variable userIsInTheMiddleOfTypingANumber is NO, thus, not going to be logged. The second call of digitPressed: will have userIsInTheMiddleOfTypingANumber = YES; and will log.

How to perform validation on textfield for phone number entered by user in iPhone?

I have an application where I have I a textfield where user enters his mobile number including his country code. The format of the mobile number to be entered is +91-9884715715. When the user enters his/her mobile number initially validation should be performed that the first value entered by user is '+' and then the number that is entered after + should not be less that 0.
But after this I am getting confused that how to get the number of numbers entered between + and -, because user enters the country code and the length of numbers entered between + and - must be dynamic not static.
Try this ., might help you
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:
(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (textField == self.yourphoneNumberfield) {
NSArray *sep = [newString componentsSeparatedByString:#"-"];
if([sep count] >= 2)
{
countryCode = [NSString stringWithFormat:#"%#",[sep objectAtIndex:0]];
if ([[countryCode substringToIndex:1] isEqualToString:#"+"]) {
phoneNumber = [NSString stringWithFormat:#"%#",[sep objectAtIndex:1]];
return ([countryCode length]+[phoneNumber length]);
}
}
}
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
NSLog(#"Phone Number : %#",phoneNumber);
if (textField == self.yourphoneNumberfield) {
if ([phoneNumber length]<10)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"UIAlertView" message:#"Please Enter a Valid Mobile number" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
return YES;
}
Try This:
NSString *code=#"+91-99999999";
NSRange rr2 = [code rangeOfString:#"+"];
NSRange rr3 = [code rangeOfString:#"-"];
int lengt = rr3.location - rr2.location - rr2.length;
int location = rr2.location + rr2.length;
NSRange aa;
aa.location = location;
aa.length = lengt;
code = [code substringWithRange:aa];
NSLog(#"%#",code);
Goto XIB interface Builder and open xib document select ur phone number type textfield and go to textfield attribute, In the Text Input Traits, select Keyboard option from Default to Phone Pad.
// limit the input to only the stuff in this character set, so no emoji or any other insane characters
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:#"1234567890"];
if ([string rangeOfCharacterFromSet:set].location == NSNotFound) {
return NO;
}
Refer #Bala's answer
NSString *call = #"+91-9884715715";
// Search for the "+a" starting at the end of string
NSRange range = [call rangeOfString:#"+" options:NSBackwardsSearch];
// What did we find
if (range.length > 0)
NSLog(#"Range is: %#", NSStringFromRange(range));
Edit
Refer following link: TextField Validation With Regular Expression
Change the line
- (BOOL)validateInputWithString:(NSString *)aString
{
NSString * const regularExpression = #"^([+]{1})([0-9]{2,6})([-]{1})([0-9]{10})$";
NSError *error = NULL;
Add the code
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
char *x = (char*)[string UTF8String];
//NSLog(#"char index is %i",x[0]);
if([string isEqualToString:#"-"] || [string isEqualToString:#"+"] || [string isEqualToString:#"0"] || [string isEqualToString:#"1"] || [string isEqualToString:#"2"] || [string isEqualToString:#"3"] || [string isEqualToString:#"4"] || [string isEqualToString:#"5"] || [string isEqualToString:#"6"] || [string isEqualToString:#"7"] || [string isEqualToString:#"8"] || [string isEqualToString:#"9"] || x[0]==0 ) {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 18) ? NO : YES;
} else {
return NO;
}
}
Edit
Tested with demo:
//// Button Press Event
-(IBAction)Check:(id)sender{
BOOL check = [self validateInputWithString:TextField.text];
if(check == YES){
NSLog(#"Hii");
NSString *string= [NSString stringWithFormat:#"%#", TextField.text];
NSArray *first = [string componentsSeparatedByString:#"-"];
NSString *second = [first objectAtIndex:1];
NSString *third = [first objectAtIndex:0];
if([second length] < 11){
NSLog(#"bang");
}
else{
NSLog(#"Fault");
}
if([third length] > 3 || [third length] < 7){
NSLog(#"Bang");
}
else{
NSLog(#"fault");
}
}
else{
NSLog(#"FAULT");
}
}

Numberpad backspace not updating textField.text.length

I'm currently formatting my a textfield in xcode, every 5th character I add a hyphen.
However I'm having alot of trouble I am currently wanting to check my textfields.text.length then once the length reaches 23 characters the submit button is press-able. So far this works where I have trouble is say if the user enters 23 characters and the button is press-able if the user decided to go back and delete one character there is nothing to update the new text length as I don't know how to catch the delete button of the numberpad... Dose anyone know how to do this?
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *separator = #"-";
int seperatorInterval = 5;
NSString *originalString = [regTextField.text stringByReplacingOccurrencesOfString:separator withString:#""];
if (![originalString isEqualToString:#""] && ![string isEqualToString:#""]) {
NSString *lastChar = [regTextField.text substringFromIndex:[regTextField.text length] - 1];
int modulus = [originalString length] % seperatorInterval;
[self validateTextFields];
if (![lastChar isEqualToString:separator] && modulus == 0) {
regTextField.text = [regTextField.text stringByAppendingString:separator];
}
}
[self validateTextFields];
return YES;
}
-(IBAction) validateTextFields {
if (regTextField.text.length >= 22){
[submitButton setEnabled:YES]; //enables submitButton
}
else {
[submitButton setEnabled:NO]; //disables submitButton
}
}
Try something like this:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// Any new character added is passed in as the "text" parameter
if (!([text isEqualToString:#""] && range.length == 1) && [textView.text length] >=140 ) {
return NO;
}
// For any other character return TRUE so that the text gets added to the view
return YES;
}
Where the block:
([text isEqualToString:#""] && range.length == 1)
Is the check for the backspace.
Capturing the backspace on the Number Pad Keyboard

Validate against empty UITextField?

What is the value of a UITextField when it is empty? I can't seem to get this right.
I've tried (where `phraseBox' it the name of the said UITextField
if(phraseBox.text != #""){
and
if(phraseBox.text != nil){
What am I missing?
// Check to see if it's blank
if([phraseBox.text isEqualToString:#""]) {
// There's no text in the box.
}
// Check to see if it's NOT blank
if(![phraseBox.text isEqualToString:#""]) {
// There's text in the box.
}
found this at apple discussions when searching for the same thing,thought ill post it here too.
check the length of the string :
NSString *value = textField.text;
if([value length] == 0) {
}
or optionally trim whitespaces from it before validation,so user cannot enter spaces instead.works well for usernames.
NSString *value = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if([value length] == 0) {
// Alert the user they forgot something
}
Try following code
textField.text is a string value so we are checking it like this
if([txtPhraseBox.text isEqualToString:#""])
{
// There's no text in the box.
}
else
{
NSLog(#"Text Field Text == : %# ",txtPhraseBox.text);
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSString *fullText = [textField.text stringByAppendingString:string];
if ((range.location == 0) && [self isABackSpace:string]) {
//the textFiled will be empty
}
return YES;
}
-(BOOL)isABackSpace:(NSString*)string {
NSString* check =#"Check";
check = [check stringByAppendingString:string];
if ([check isEqualToString:#"Check"]) {
return YES;
}
return NO;
}
Use for text field validation:
-(BOOL)validation{
if ([emailtextfield.text length] <= 0) {
[UIAlertView showAlertViewWithTitle:AlertTitle message:AlertWhenemailblank];
return NO; }
return YES;}
Actually, I ran into slight problems using Raphael's approach with multiple text fields. Here's what I came up with:
if ((usernameTextField.text.length > 0) && (passwordTextField.text.length > 0)) {
loginButton.enabled = YES;
} else {
loginButton.enabled = NO;
}
Validation against empty UITextfield. if you don't want that UITextField should not accept blank white spaces. Use this code snippet:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string];
NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
if ([resultingString rangeOfCharacterFromSet:whitespaceSet].location == NSNotFound) {
return YES;
} else {
return NO;
}
}