Making an NSTextField act like a "microwave timer", store as NSNumber - objective-c

What I'm trying to accomplish is two fold:
I want my NSTextField to act like a "microwave timer" and update as characters are entered. So the field would default to "00:00" (minutes, seconds). When a 1 is pressed, the field would look like "00:01". Then a 4 is pressed and "00:14". A 2 is pressed and "01:42".
I want to store this as an NSNumber on the controller as the number of seconds in the timer. So if the text field shows "10:00", then the value on the property would be 600.
Seems like NSFormatter is a good place to be looking, but my attempts have been full of fail. Any help is welcome. Thanks!

a) Divide the value of your timer property into minutes and seconds. Then use an NSFormatter setup with two leading zeros to create strings from the two numbers (see: How to specify decimal places when formatting NSNumber objects?). Then use stringWithFormat: to put the two strings on either side of a colon.
b) Or just divide out each digit of the four (in your example) numbers and put that number in a string with the colon in the correct spot. (so first the tens of minutes, then the minutes, then the tens of seconds, then the seconds)
Either of the above could be in a custom NSFormatter subclass set on the NSTextField or in a method in the view's controller called by an observer (using KVO) of your timer property.

Related

Prevent NSTextField from inserting commas when setting intValue

In my app I have an NSTextField into which the user can type a value for "Year". This works fine and I save the value off to a file as an integer. However, when I read this value back and place it back into the text field for editing, like so:
YearTextField.intValue = dataFromFile.year;
The year is comma separated, so an original value of 2020 appears as: 2,020
I know I can stringWithFormat the value into an NSString and then set the stringValue of the NSTextField, but is there a more straightforward way to simply tell YearTextField to not insert commas when setting the value?
Thanks!

NSTextField: integerValue behaving inconsistently

I have found that [NSTextField integerValue] behaves differently with values containing thousands separators depending on how the value was set.
(I am in Germany, so my thousands separator in these examples is a point ".").
If I call [myTextField setIntegerValue:4567], the text field will contain 4.567 (with tousands separator), and [myTextField integerValue] returns 4567.
If I type the value 4.567 info the text field manually, or use [myTextField setStringValue:#"4.567"], then [myTextField integerValue] returns just 4.
Apparently it stops parsing the number at the thousands separator, even though it inserts such a separator itself when calling setIntegerValue.
So I have actually two questions:
Is there a setting or other easy way that I can prevent it to format the number when using -setIntegerValue: ?
Can I "enable" the number parsing to understand/accept thousands separators when calling -integerValue? Or, if not, what would be the simplest way to parse a number with thousands separator from an NSString?
Add a Number Formatter (NSNumberFormatter) to the text field in the storyboard or XIB. This makes the contents of the cell and objectValue of the text field a NSNumber instead of a NSString.
Is there a setting or other easy way that I can prevent it to format the number when using -setIntegerValue: ?
Switch off Grouping Separator of the formatter.
Can I "enable" the number parsing to understand/accept thousands separators when calling -integerValue?
Switch on Grouping Separator of the formatter and set Primary Grouping to 3.
Or, if not, what would be the simplest way to parse a number with thousands separator from an NSString?
Use a NSNumberFormatter, see the class reference and Number Formatters.
integerValue is not locale-aware, it will always use . as the decimal point.
You should use NSNumberFormatter.numberFromString: instead.
Link to NSNumberFormatter class reference.

Objective C: calculator app - currentNumber = currentNumber * 10 + digit;

I am reading "Programming in Objective-C" 4th Ed. by Stephen G. Kochan. In the book, there is a sample code for creating a Calculator application for the iPhone. I understand the code, at least 90% of it. - There is a Fraction class that has methods to store fraction objects and that describe how to perform different basic fraction arithmetic operations
- In addition to that, there is a calculator class that runs the appropriate methods from the Fraction class depending on whether the user is trying to sum, divide etc.
The view controller has the following method for when the user presses a number in the interface:
-(IBAction)clickDigit:(UIButton *)sender {
int digit = sender.tag; //sender or the argument inthis case is the button
[self processDigit:digit];
}
As you see this method is now called:
-(void) processDigit:(int)digit {
currentNumber = currentNumber * 10 + digit;
[displayString appendString:
[NSString stringWithFormat:#"%i", digit]];
display.text = displayString;
}
My ONLY question (and probably the one with the most simple answer) is: Why is currentNumber always multiplied by 10? The value of currentNumber is always 0 by the time the compiler enters the method above (I verified this using the debugger in XCode) so i dont get why we even have to multiply it by 10. I did delete that multiplication and the results are incorrect, i just cannot figure out why yet.
Thank you
Maybe the best way to think about this is with an example.
Imagine the user has clicked the following digits in order: 1, 2. Then, assuming the code is working, the numeric value of currentNumber should be 12.
Now imagine the user next clicks on '3'. Now you want the value to be 123. You can get this by multiplying the previous value (12) by 10 and then adding the 3. If they then click on a '4', the value should become 1,234, which is achieved by 10 * 123 + 4. And so on.
Imagine your calculator has "123" on the screen and you want to turn this into "1234". If it was a string you can add "4" to the end, and it works fine. But it's an integer, and if you add 4 to the integer value you get 127. So what you do is take 123, multiply by 10 to give you 1230, then add the 4 to get 1234.
The debugger must be misleading you, as it sometimes does.
Consider that if taking the multiplication out makes the result wrong then the multiplication obviously does something!
Try the following for debugging only:
int lastCurrentNumber = currentNumber;
currentNumber = 10 * lastCurrentNumber + digit;
Now in the debugger check the values of lastCurrentNumber, currentNumber and digit as you step through these two statements.
The *10 is used to move digits through the various places in decimal numbers (tens place, hundreds place, etc.)
It's kind of a "hack" to avoid having to use an array. Other calculator apps "pop" and "push" digits onto an array, this allows for more powerful operations as well as manipulation of non-decimal numbers (binary, hex, etc.)
The free iOS development course on iTunes U includes a calculator app that uses an array. If you'd like to compare the difference, you can download the source code here:
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/
It also has a Fraction object but doesn't use the *10 method.

The textfield displayed a value which is the calculate result from an action, how to bind the value to core data

I'm working on mac app project. I have a table and a textfield, one column named price, and the textfield names price as well. I binding the column price to a controller with keypath arrangedObject, for the textfield, I bind to the same controller, with keypath selection. It worked. It can read the data from user input to textfield.
However now, I need to calculate the number then display the number to the textfield, and then bind the value. so that the column can also display the value. How to do that?
If I understand you correctly, you are already successfully doing the bind part. So what you need to do is set a calculated value in the text field - which, due to the binding, will then also be shown in the appropriate cell in the table.
To set a value in a text field, you create a new outlet (yourTextFieldName) in the controller class by control-click dragging to a blank line in the controller class .h file - but I'm guessing you are far enough along to know that. Then to set the value use
[yourTextFieldName aString];
Presuming that your data is numeric (as you refer to computation) you may have to convert it to a string first, possibly with
[[NSString alloc] initWithFormat:#"%#", numericValue ]; // with NSNumber *numericValue or %g if it is a float, or...

How do I set Cocoa NSLevelIndicatorCell values?

I have a tableview. One of the columns in the tableview uses an NSLevelIndicatorCell.
I want to be able to allow the user to edit the warn and critical values for the level indicator such that when they enter a value into a a "warning level" textbox, it changes the warn value of the level indicators being displayed in ALL of the tableview's rows.
I am very much a newbie with Objective-C so all I can figure out so far is that I must need a delegate method to watch the textbox BUT if I succeed in doing that, how on earth do I send the new value to the particular tableview column so that the update happens to ALL of the rows (i.e. how do I send what message to the tableview and target a cell within a column within a tableview)?
Here is the code to the solution I came up with should anyone need it.
- (IBAction)setWarningLevel:(id)sender {
double v;
NSScanner *ns = [NSScanner scannerWithString:[warnLevel stringValue]];
[ns scanDouble:&v];
[levelIndicator setWarningValue:v];
}
This is a textbook case for using Cocoa bindings. Just bind the value of the text field to the NSLevelIndicatorCell in your table view (do that in Interface Builder). The updates should happen automagically.
I think it should apply for all the cells in the table view if you apply the binding to the cell in IB. However if it doesn't, you will need to write a couple lines of code that set up the binding every time a new row in the table is created. That link above will explain everything in detail, but basically you will be setting up a Key-Value Observer relationship in code between the text field and the instance of the level indicator in the row being created.
I think you may have overdone it.
NSTextField subclasses NSControl, so you need to look in the docs for NSControl for a useful function.
Try re-writing it like this; assuming you're taking the value from a warnLevel textfield.
- (IBAction)setWarningLevel:(id)sender {
double v = [warnLevel doubleValue];
[levelIndicator setWarningValue:v];
}
Although this is usually shortened to this;
- (IBAction)setWarningLevel:(id)sender {
[levelIndicator setWarningValue:[warnLevel doubleValue]];
}
You should probably have some validation that the textfield has a valid number. If you're only choosing a couple of numbers have a look at using a stepper control.
Usually, with Cocoa, if you feel like you're jumping through too many hoops, there is sometimes an easier way.
Usually ;-)