If statement reading currentTitle of sender UIButton isn't working - objective-c

I'm trying to get the calculator app I've built to trigger some simple code if it detects I pushed the decimal point button (to only allow one decimal point per value). However, for some reason, these if statements regarding digit aren't being triggered. I've NSLogged the output of it and it is indeed showing as "." so I can't quite figure out why. I've changed the . to any other value too, nothing seems to trigger 'em.
NSString *digit = [sender currentTitle];
if (self.userIsInTheMiddleOfEnteringANumber) {
self.display.text = [[[self display] text] stringByAppendingString:digit]; // All in one line!
NSLog(#"Digit is equal to %#",digit);
if (digit == #".") NSLog(#"That was a decimal point!");
} else {
self.display.text = digit;
if (digit == #".") NSLog(#"You can't begin a number with a decimal!");
self.userIsInTheMiddleOfEnteringANumber = YES; // If they aren't typing, now they are!
}

Dont compare strings with ==. Use isEqualToString:
if ([digit isEqualToString:#"."])
== checks for pointer equality, not string equality.

== doesn't work:
if (digit == #".")
For string use:
if([digit isEqualToString:#".")

Related

Prevent Users from Entering Multiple Decimals In A String

I am doing a button calculator app (still) in which users press a button, I store a value into a string to display in a text box, and then convert into floats when the user enters the = button.
Works great for the most part, except I cannot figure out how to prevent users from entering more than one decimal in a single string. I thought about passing it to a BOOL method, but none of my class materials cover methods at all.
I looked at this Stack overflow question, but trying to amend the code for my own just resulted in a whole bunch of errors. Does anyone have any advice?
-(IBAction) decimal
{
NSString *decVal =#".";
NSRange range = [decVal rangeOfString:#"."];
if (range.location==NSNotFound)
{
display.text = [display.text stringByAppendingString:decVal];
}
else
{
NSLog(#"You can't enter more than one decimal");
}
}
-(IBAction) decimal
{
static NSString *decVal =#".";
NSRange range = [display.text rangeOfString:decVal];
if (NSNotFound == range.location)
{
display.text = [display.text stringByAppendingString:decVal];
}
else
{
NSLog(#"You can't enter more than one decimal");
}
}
You have missed in one string. [decVal rangeOfString:#"."] will always return range {0,1}.
One way to handle this would be to set the delegate of the text field/view and then implement shouldChangeCharactersInRange in the delegate.
For example, you could do something like this:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
BOOL hasPeriod = ([textField.text rangeOfString:#"."].location != NSNotFound);
BOOL removingPeriod = ([[textField.text substringWithRange:range]
rangeOfString:#"."].location != NSNotFound);
BOOL addingPeriod = ([string rangeOfString:#"."].location != NSNotFound);
return (hasPeriod && addingPeriod && !removingPeriod) ? NO : YES;
}
This won't throw an error. It just won't allow a second period. You could easily add in the error message, though.

Backspacing a Number in a Calculator App

Can someone explain this code
- (IBAction)backspacePressed {
self.display.text =[self.display.text substringToIndex:
[self.display.text length] - 1];
if ( [self.display.text isEqualToString:#""]
|| [self.display.text isEqualToString:#"-"]) {
self.display.text = #"0";
self.userIsInTheMiddleOfEnteringNumber = NO;
}
}
I don't get what the 2 lines mean in objective c. || Also, I don't get the meaning of substringToIndex. How does a programmer know to use substringToIndex out of all the different methods in the documentation I saw substringFromIndex etc. There are so many. Is this saying that the strings in the index are counted and -1 means it deletes a string? How would the meaning in the apples documentation relate to deleting a character ?
Comments supplied with explanation of code...
- (IBAction)backspacePressed
{
// This is setting the contents of self.display (a UITextField I expect) to
// its former string, less the last character. It has a bug, in that what
// happens if the field is empty and length == 0? I don't think substringToIndex
// will like being passed -1...
self.display.text =[self.display.text substringToIndex:
[self.display.text length] - 1];
// This tests if the (now modified) text is empty (better is to use the length
// method) or just contains "-", and if so sets the text to "0", and sets some
// other instance variable, the meaning of which is unknown without further code.
if ( [self.display.text isEqualToString:#""]
|| [self.display.text isEqualToString:#"-"]) {
self.display.text = #"0";
self.userIsInTheMiddleOfEnteringNumber = NO;
}
}
|| is an OR operator. At least one of the statements has to be true.
Look at Apple's documentation for the substringToIndex: method
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html
This is stuff you could find easily with a google search.

CS193p-Adding backspace to a calculator

I recently started following the online course on iPhone development from Stanford University on iTunes U.
I'm trying to do the homework assignments now for the first couple of lectures. I followed through the walkthrough where I built a basic calculator, but now I'm trying the first assignment and I can't seem to work it out. It's a follows:
Implement a “backspace” button for the user to press if they hit the wrong digit button. This is not intended to be “undo,” so if they hit
the wrong operation button, they are out of luck! It’s up to you to
decided how to handle the case where they backspace away the entire
number they are in the middle of entering, but having the display go
completely blank is probably not very user-friendly.
I followed this: Creating backspace on iOS calculator
So the code is
-(IBAction)backspacePressed:(UIButton *)sender {
NSMutableString *string = (NSMutableString*)[display text];
int length = [string length];
NSString *temp = [string substringToIndex:length-1];
[display setText:[NSString stringWithFormat:#"%#",temp]];
}
My question is shouldn't I check whether my last is a digit or operand? If operand, no execution and if digit, remove it...
First of all, there are several unnecessary steps in that code... And to answer your question, yes, you should check for an operand. Here is how I would write that method with a check:
NSString *text = [display text];
int length = [text length];
unichar c = [text characterAtIndex:length];
NSCharacterSet *digits = [NSCharacterSet decimalCharacterSet];
if ([digits characterIsMember:c] || c == '.') {
NSString *temp = [[display text] substringToIndex:length-1];
[display setText:temp];
}
I'm also going through the fall 2011 class on iTunes U.
The walk through gives us an instance variable userIsInTheMiddleOfEnteringANumber so I just checked to see if that is YES.
- (IBAction)backspacePressed {
if (self.userIsInTheMiddleOfEnteringANumber) {
if ([self.display.text length] > 1) {
self.display.text = [self.display.text substringToIndex:[self.display.text length] - 1];
} else {
self.display.text = #"0";
self.userIsInTheMiddleOfEnteringANumber = NO;
}
}
}
I used the approach taken by Joe_Schmoe, which is straightforward. (just remove characters in the dispaly until you reach the end).
If the user continues pressing 'Clear Error', I removed an item from the stack as well.
I have just started on the course myself, this post is quite old now but my solution might help others, food for thought if nothing else:
- (IBAction)deletePressed:(id)sender
{
NSString *displayText = [display text];
int length = [displayText length];
if (length != 1) {
NSString *newDisplayText = [displayText substringToIndex:length-1];
[display setText:[NSString stringWithFormat:#"%#",newDisplayText]];
} else {
userIsInTheMiddleOfEnteringANumber = NO;
NSString *newDisplayText = #"0";
[display setText:[NSString stringWithFormat:#"%#",newDisplayText]];
}
}

determine if a string is a number with NSScanner

i'm trying to find out if my string contains any floatValue, and resign the first responder if it's the case, if it's not, the textfield keyboard should stay on screen.
This code always hides the keyboard, even if it's not a floatValue : do you know how to make it work?
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSScanner *scan = [NSScanner scannerWithString:[textField text]];
if ([scan scanFloat:NULL]){
[password resignFirstResponder];
[passwordLength resignFirstResponder];
return YES;
} else {
return NO;
}
}
Also, i haven't tried with loops but this is a beginning, if you have any idea :
BOOL doesStringContain(NSString* string, NSString* string2){
for (int i=0; i<[string length]; i++) {
NSString* chr = [string substringWithRange:NSMakeRange(i, 1)];
for (int j=0; j<[string2 length]; j++){
if([chr isEqualToString:j])
return TRUE;
}
}
return FALSE;
}
Thanks a lot
NSScanner will expect that your float be at the beginning of your string. So to combat that we use setCharactersToBeStripped.
setCharactersToBeStripped will filter out all non-numeric non-period characters from the string so that all you're left with to scan is the number that you're looking for.
NSScanner will match int values (without the .) as well as it will equate an int 123 to 123.00000.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSScanner *scan = [NSScanner scannerWithString:[textField text]];
[scan setCharactersToBeSkipped:[[NSCharacterSet characterSetWithCharactersInString:#"1234567890."] invertedSet]];
float f;
if ([scan scanFloat:&f]){
NSLog(#"Scanned a float %f",f);
[textField resignFirstResponder];
return YES;
} else {
NSLog(#"Did not scan a float");
return NO;
}
}
If you want to check if there are non-numeric characters vs only numerics then try :
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSCharacterSet *withFloats = [NSCharacterSet characterSetWithCharactersInString:#"0123456789."];
NSCharacterSet *withoutFloats = [NSCharacterSet decimalDigitCharacterSet];
// Change withFloats <-> withoutFloats depending on your need
NSString *newString = [[textField.text componentsSeparatedByCharactersInSet:withFloats] componentsJoinedByString:#""];
NSLog(#"newString %#", newString);
if ([newString isEqualToString:#""]){
NSLog(#"Scanned a numeric");
[textField resignFirstResponder];
return YES;
} else {
NSLog(#"Did not scan a numeric");
return NO;
}
}
You are implementing the wrong delegate method. textFieldShouldReturn: is called when the user hits the return key. (Not when control is trying to "return from" the field, in case that's what you were thinking.)
You should implement textFieldShouldEndEditing: instead. That is where you can (try to) stop the text field from losing first responder status, and keep the keyboard up. Just check the text like you are doing, and return YES or NO (let the system update first responder status).
If you want the return key to dismiss the keyboard if input is valid, you should call endEditing: there. Like this:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return [textField endEditing:NO];
}
The NO parameter means don't force it, basically allowing your textFieldShouldEndEditing: code to check the text first. (You should always call endEditing: if you can, rather than resignFirstResponder.)
Note that, for various reasons, the text field might be forced to give up first responder status anyway, so even if you're validating input in this way, be prepared to validate it again before you save it to disk or send it over the network or whatever you want to do with it.

Weird cocoa bug?

Hey folks, beneath is a piece of code i used for a school assignment.
Whenever I enter a word, with an O in it (which is a capital o), it fails!
Whenever there is one or more capital O's in this program, it returns false and logs : sentence not a palindrome.
A palindrome, for the people that dont know what a palindrome is, is a word that is the same read left from right, and backwards. (e.g. lol, kayak, reviver etc)
I found this bug when trying to check the 'oldest' palindrome ever found: SATOR AREPO TENET OPERA ROTAS.
When I change all the capital o's to lowercase o's, it works, and returns true.
Let me state clearly, with this piece of code ALL sentences/words with capital O's return false. A single capital o is enough to fail this program.
-(BOOL)testForPalindrome:(NSString *)s position:(NSInteger)pos {
NSString *string = s;
NSInteger position = pos;
NSInteger stringLength = [string length];
NSString *charOne = [string substringFromIndex:position];
charOne = [charOne substringToIndex:1];
NSString *charTwo = [string substringFromIndex:(stringLength - 1 - position)];
charTwo = [charTwo substringToIndex:1];
if(position > (stringLength / 2)) {
NSString *printableString = [NSString stringWithFormat:#"De following word or sentence is a palindrome: \n\n%#", string];
NSLog(#"%# is a palindrome.", string);
[textField setStringValue:printableString];
return YES;
}
if(charOne != charTwo) {
NSLog(#"%#, %#", charOne, charTwo);
NSLog(#"%i", position);
NSLog(#"%# is not a palindrome.", string);
return NO;
}
return [self testForPalindrome:string position:position+1];
}
So, is this some weird bug in Cocoa?
Or am I missing something?
B
This of course is not a bug in Cocoa, as you probably knew deep down inside.
Your compare method is causing this 'bug in Cocoa', you're comparing the addresses of charOne and charTwo. Instead you should compare the contents of the string with the isEqualToString message.
Use:
if(![charOne isEqualToString:charTwo]) {
Instead of:
if(charOne != charTwo) {
Edit: tested it in a test project and can confirm this is the problem.
Don't use charOne != charTwo
Instead use one of the NSString Compare Methods.
if ([charOne caseInsensitiveCompare:charTwo] != NSOrderedSame)
It may also have to do with localization (but I doubt it).