4

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;
    }
}
chrispalle
  • 45
  • 7

5 Answers5

2
- (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.

Jamie
  • 5,090
  • 28
  • 26
  • hrm, moved things around a bit- definitely optimized, but now I'm a little unclear as to what's happening in the BOOL declaration. It does seem to work better, but for some reason, when I tap an operator (not shown) it crashes... – chrispalle Apr 21 '11 at 14:58
  • nvrmnd. I see what I did. Didn't replace the final "else." it's not taking any input now, though... back to tinkerin' :-) – chrispalle Apr 21 '11 at 15:12
  • 1
    If it's not taking any input, you may want to set a breakpoint and see if it's even calling this method and what the state of your ivars are. – Jamie Apr 21 '11 at 18:15
  • Looks like I missed one curly brace in there. Put it up there now! The else should run as long as the user is not in the middle of a number. – Jamie Apr 21 '11 at 18:35
  • Thanks, Jamie! That was actually helpful in a different way- got me to figure out how to set breakpoints! I had one set I didn't realize it! lol. Yes, this method is getting called when a button is pressed, but not updating the display for some reason... – chrispalle Apr 21 '11 at 19:01
  • Actually, this is getting much closer... it wasn't working before so I didn't up it, but now it's just about there. thanks, jamie – chrispalle Apr 29 '11 at 20:54
  • No problem. If you're still having issues, let me know and I can try to help. – Jamie Apr 29 '11 at 21:01
  • The only problem is that we can only enter a decimal if it's the first character. If there is already a digit of any length in the display, it won't allow a decimal. – chrispalle Apr 29 '11 at 21:14
  • Also, I have a 0 in the display by default. It's not appending to this zero. – chrispalle Apr 29 '11 at 21:23
  • 1
    Oops..that's the problem with doing it off the cuff and not typing it. I haven't tested it. Changed the code to search display.text for the decimal in the BOOL statement. Hope that works. – Jamie Apr 29 '11 at 21:25
  • yep. that did it. Still not appending to the initial default 0, though. – chrispalle Apr 29 '11 at 21:39
  • that's okay. This was a learning experience for me and I picked up quite a bit from each of the respondents. Thanks again, man. – chrispalle Apr 30 '11 at 18:13
2

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.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • This is really useful and thoughtful of the UX. I think for my basic purposes- just to learn Obj-C syntax, i just need to understand some of the messaging. I don't want to introduce additional methods just yet. Very thoughtful, though- thanks! – chrispalle Apr 28 '11 at 18:52
  • @chrispalle: Hope you come back to these ideas when you're ready. Glad you've taken it into consideration! Don't forget to upvote answers you like ;) (I'm shooting for the rep cap today.) – jscs Apr 28 '11 at 18:57
  • Yeah, for sure this is good. I know what I'm looking to do is n00bish, but i like to have a solid foundation. :-) – chrispalle Apr 29 '11 at 16:15
1

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

Community
  • 1
  • 1
Wolfgang Schreurs
  • 11,779
  • 7
  • 51
  • 92
  • Ah, thanks Wolfgang. I see something interesting, but not sure why it does this- if I change the operator to != in the range.location == NSNotFound, it seems to accept only one decimal. It's getting there! Thanks. – chrispalle Apr 21 '11 at 14:53
  • Well, I wrote the code like a year ago for a project and eventually it was decided by my superiors to just allow any input for numeric fields, so I never got to finish the code for numeric filtering - I'm sure you will find some bugs in it. But if you still need to improve upon this and if I got some time today or tomorrow, I could try to figure out how to make it work for you. – Wolfgang Schreurs Apr 21 '11 at 15:14
  • appreciate your help, Wolfgang (would have up-voted your response, but I don't have 15 reputation pts yet :-/). Something else is fussing now. hopefully, i'll get this today. cheers! – chrispalle Apr 21 '11 at 16:08
1

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; 
    }
}
takrl
  • 6,356
  • 3
  • 60
  • 69
Chrys
  • 11
  • 1
0


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;
    }
}
dks1725
  • 1,631
  • 1
  • 20
  • 29