Edit (1/6/09): Thanks to one of my readers, we now have a secondary mirror site for downloading the video. Download locations: Mirror 1 Mirror 2
//
// CalcModel.h
// SimpleCalc
//
// Created by Chris Ball (chris.m.ball@gmail.com) on 1/17/08.
// Copyright 2008, Chris Ball, Strainthebrain.Blogspot.Com.
// All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface CalcModel : NSObject {
float running_total;
char sign_state;
bool first_call;
}
//Establishing some public-facing properties.
@property(readwrite) float running_total;
@property(readwrite) char sign_state;
@property(readwrite) bool first_call;
//Used for computing and maintaing the model data.
- (void) computeNewDisplayVal: (float) currDisplayVal;
@end
//
// CalcModel.m
// SimpleCalc
//
// Created by Chris Ball (chris.m.ball@gmail.com) on 1/17/08.
// Copyright 2008, Chris Ball, Strainthebrain.Blogspot.Com.
// All rights reserved.
//
#import "CalcModel.h"
@implementation CalcModel
//Implicity declaring the setters/getters for our properties.
@synthesize running_total, sign_state, first_call;
- (void) computeNewDisplayVal: (float) currDisplayVal {
if (self.first_call) {
self.running_total = currDisplayVal;
self.first_call = NO;
}
else {
switch (self.sign_state) {
case 'a':
self.running_total = self.running_total + currDisplayVal;
break;
case 'd':
self.running_total = self.running_total / currDisplayVal;
break;
case 'm':
self.running_total = self.running_total * currDisplayVal;
break;
case 's':
self.running_total = self.running_total - currDisplayVal;
break;
}
}
}
@end
//
// CalcController.h
// SimpleCalc
//
// Created by Chris Ball (chris.m.ball@gmail.com) on 1/17/08.
// Copyright 2008, Chris Ball, Strainthebrain.Blogspot.Com.
// All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "CalcModel.h"
@interface CalcController : NSObject {
IBOutlet id calc_display;
IBOutlet id a_button;
IBOutlet id s_button;
IBOutlet id m_button;
IBOutlet id d_button;
CalcModel* calc_model;
}
//Mathematical button events.
- (IBAction) push_add: (id) sender;
- (IBAction) push_divide: (id) sender;
- (IBAction) push_multiply: (id) sender;
- (IBAction) push_subtract: (id) sender;
//Numeric button events.
- (IBAction) push_zero: (id) sender;
- (IBAction) push_one: (id) sender;
- (IBAction) push_two: (id) sender;
- (IBAction) push_three: (id) sender;
- (IBAction) push_four: (id) sender;
- (IBAction) push_five: (id) sender;
- (IBAction) push_six: (id) sender;
- (IBAction) push_seven: (id) sender;
- (IBAction) push_eight: (id) sender;
- (IBAction) push_nine: (id) sender;
//Internal functions.
- (void) push_number: (id) sender;
- (BOOL) sign_pushed;
- (void) check_calc_model;
//Other button events.
- (IBAction) push_clear: (id) sender;
- (IBAction) push_decimal: (id) sender;
- (IBAction) push_equal: (id) sender;
@end
//
// CalcController.m
// SimpleCalc
//
// Created by Chris Ball (chris.m.ball@gmail.com) on 1/17/08.
// Copyright 2008, Chris Ball, Strainthebrain.Blogspot.Com.
// All rights reserved.
//
#import "CalcController.h"
#import <RegexKit/RegexKit.h>
@implementation CalcController
- (IBAction) push_add: (id) sender {
if ([m_button state] == NSOnState || [s_button state] == NSOnState || [d_button state] == NSOnState) {
[a_button setState:NSOffState];
}
else {
[self check_calc_model];
if (!calc_model.first_call)
[self push_equal:0];
calc_model.sign_state = 'a';
[calc_model computeNewDisplayVal:[calc_display floatValue]];
[calc_display setFloatValue:calc_model.running_total];
}
}
- (IBAction) push_divide: (id) sender {
if ([a_button state] == NSOnState || [s_button state] == NSOnState || [m_button state] == NSOnState) {
[d_button setState:NSOffState];
}
else {
[self check_calc_model];
if (!calc_model.first_call)
[self push_equal:0];
calc_model.sign_state = 'd';
[calc_model computeNewDisplayVal:[calc_display floatValue]];
[calc_display setFloatValue:calc_model.running_total];
}
}
- (IBAction) push_multiply: (id) sender {
if ([a_button state] == NSOnState || [s_button state] == NSOnState || [d_button state] == NSOnState)
[m_button setState:NSOffState];
else {
[self check_calc_model];
if (!calc_model.first_call)
[self push_equal:0];
calc_model.sign_state = 'm';
[calc_model computeNewDisplayVal:[calc_display floatValue]];
[calc_display setFloatValue:calc_model.running_total];
}
}
- (IBAction) push_subtract: (id) sender {
if ([a_button state] == NSOnState || [m_button state] == NSOnState || [d_button state] == NSOnState)
[s_button setState:NSOffState];
else {
[self check_calc_model];
if (!calc_model.first_call)
[self push_equal:0];
calc_model.sign_state = 's';
[calc_model computeNewDisplayVal:[calc_display floatValue]];
[calc_display setFloatValue:calc_model.running_total];
}
}
- (IBAction) push_zero: (id) sender {
[self push_number:sender];
}
- (IBAction) push_one: (id) sender {
[self push_number:sender];
}
- (IBAction) push_two: (id) sender {
[self push_number:sender];
}
- (IBAction) push_three: (id) sender {
[self push_number:sender];
}
- (IBAction) push_four: (id) sender {
[self push_number:sender];
}
- (IBAction) push_five: (id) sender {
[self push_number:sender];
}
- (IBAction) push_six: (id) sender {
[self push_number:sender];
}
- (IBAction) push_seven: (id) sender {
[self push_number:sender];
}
- (IBAction) push_eight: (id) sender {
[self push_number:sender];
}
- (IBAction) push_nine: (id) sender {
[self push_number:sender];
}
- (void) push_number: (id) sender {
if ([self sign_pushed]) {
[a_button setState:0];
[d_button setState:0];
[m_button setState:0];
[s_button setState:0];
[calc_display setStringValue:@""];
}
if ([[calc_display stringValue] compare:@"0"] && !calc_model.first_call)
[calc_display setStringValue:[[calc_display stringValue] stringByAppendingString:[sender title]]];
else
[calc_display setStringValue:[sender title]];
}
- (BOOL) sign_pushed {
if ([a_button state] == NSOnState || [s_button state] == NSOnState || [m_button state] == NSOnState || [d_button state] == NSOnState)
return YES;
else return NO;
}
- (void) check_calc_model {
@try {
if (calc_model == nil) {
calc_model = [[CalcModel alloc] init];
calc_model.first_call = YES;
}
}
@catch (NSException *ex) {
NSLog(@"check_calc_model: Caught %@: %@", [ex name], [ex reason]);
}
}
- (IBAction) push_clear: (id) sender {
if (calc_model != nil) {
[calc_model release];
calc_model = nil;
}
[calc_display setIntValue:0];
if ([self sign_pushed]) {
[a_button setState:0];
[d_button setState:0];
[m_button setState:0];
[s_button setState:0];
}
}
- (IBAction) push_decimal: (id) sender {
if ([self sign_pushed]) {
[a_button setState:0];
[d_button setState:0];
[m_button setState:0];
[s_button setState:0];
[calc_display setStringValue:@""];
}
if (![[calc_display stringValue] isMatchedByRegex:@"[.]"])
[calc_display setStringValue:[[calc_display stringValue] stringByAppendingString:@"."]];
}
- (IBAction) push_equal: (id) sender {
[self check_calc_model];
[calc_model computeNewDisplayVal:[calc_display floatValue]];
[calc_display setFloatValue:calc_model.running_total];
calc_model.first_call = YES;
}
@end
40 comments:
Thank you so much for this tutorial!! One thing, though: where's the Chopin!?!? :(
Glad you enjoyed the tutorial. It took a long time to put together, given all of the things involved.
I originally strung together about 3 long Chopin pieces to fill in the 25 minutes, but I felt it ended up being a bit distracting for this particular screencast. I suppose I could have left it in at a lower volume, but each time I made a video render, it took about an hour, so I just yanked the music out for this one. Maybe next time :)
Yeah, there was a lot more talking in this one so music probably would have been distracting.
So I've been trying all day to get this program to run, and I can't. It does seem to partially work. For example, the calculator won't let me push more than one of the 'sign' buttons at a time.
However, nothing ever appears on the display no matter what I push. :(
Just so that my other viewers have more background information, "the thorn family" figured out his problem, he had failed to create the IBOutlet for the calc_display when setting up the GUI in Interface Builder.
So if you find yourself in the same boat, make sure you've properly set up all of your Actions and Outlets! :)
Newbie Question here...What do you do with the main.m? Do you leave it empty, or do you remove it?
Hi Darin,
main.m in this case can be left exactly how xcode generates it. There's no need to add additional code to it. Don't remove it.
Thanks for the time, effort, and knowledge that you've used to make this tutorial. I'm going to watch the YouTube parts again, and then this last large segment carefully as I start with following along with the code.
Would it be possible to post the Parts 1 & 2 in same hi-res format at your site. I realize YouTube put certain limitations on timing and resolution, hence your departure for the last part. Hope you still have those originals on the first two... That would be much easier on the eyes!!!
Thanks again...
Well, I can meet you half way on that request. The first 2 parts that you watched on youtube are also on my blog here and the resolution for those is better than youtube, but worse than my final part 3.
I am pretty limited in terms of time spare right now, and any breathing moments I do have I like to spend on new posts and such :). Glad you've enjoyed the series my friend!
I have come across these similar python mathematical issues within objective-c.
Your issues with the floater calculations being estimated could easily be solved by treating the decimal as a delimiter. Then you can treat both integers as whole numbers and carry the notation on the right side to the left if it increases/decreases its max/min digits value (useful when performing addition/subtraction). Your multiplication/division functions should calculate just fine though.
No need to reinvent the wheel; however, as there may be an even easier math library to include that magically fixes these issues.
Since mac is now developed under a Unix kernel; moreover, BSD, you could check out the terminal application known as BC. Not to compliment this application as the end all fabulous math tool-- it has its own truncating issues with floaters (example 5/2 = 2).
Great videos however! Very useful for objective-c within XCode learners.
*please excuse my deleted post, there was an important piece left out which I wanted to include in this post.
To begin with, I'm pretty new to Cocoa/Objective-C. I have done some Java development. I have a question/comment about your check to see if there's a decimal point in the string (in the "push_decimal" function) - Instead of using RegexKit, couldn't you have used some sort of indexOf(".") function on the string? Like I said, I'm new to Objective-C, but I think the function for this would be rangeOfString: of NSString. It's the only one I could find that resembles Java's indexOf() function.
Awesome tutorial, btw. Very helpful when learning Cocoa/Objective-C.
I haven't had a chance to watch the video (just read the code), but your example really helped clear some things up in my mind. Objective-C itself is straight forward, but Cocoa isn't always, and your tutorial helped me start to figure out some of those basic problems!
(Bad english from a spanish dude aproaching)
Hi!
I'm getting my first steps on OsX programming and i felt completely lost.
Then i found your tutorial. At last! Something from scratch!
Thx for all. ;)
Best regards from Spain!
(eof)
Firstly, this is a great tutorial! I was struggling to get anywhere with xcode until your tutorials showed the missing link that was Actions and Outlets in the Interface Builder!
I found changing the float references in your code, to double fixed the rounding errors.
Also, as posted above, you can use rangeOfString to find an existing decimal point, rather than using a regex.
if ([[calc_display stringValue] rangeOfString:@"."].location == NSNotFound)
{
// Add decimal point
}
I did find your method helpful, though as I now know how to link in 3rd party frameworks :-)
Hope you make more of these tutorials in future.
I love this tutorial!!! Although, because it's my first crack at this I've got an error message saying "error: 'm_button' undeclared (first use in this function)
After spending 2 days going over all the code to makes sure it's that same as yours, it still doesn't work. Any suggestions?
Otherwise I might start from scratch and see how I go.
Thanks again for making this tute, it's fantastic.
Hi Stephen,
If you're receiving that error, odds are one of two things went wrong:
1. Your declaration in the controller class header file is missing "IBOutlet id m_button;"
OR
2. When you set up your outlets in your NIB file, you may have potentially messed up and want to double-check to verify that things look correct from within interface builder.
Let me know how that works out for you.
Hi Chris,
I've just started programming (or learning) and this blog is amazing! I started off by learning something from Kevin Vinck on Vimeo, but then I thought, "Let's do something more challenging! Like a calculator app! How hard could that be!?"
So days later, I concluded that this wasn't happening without some clue as to what I'm doing.
Honestly, you're a great teacher!
I also have run into a slight problem.
My buttons are named slightly different in comparison to your buttons.
push_zero > push_num0
push_one > push_num1
push_two > push_num2
and so on.
And the IB functions look like this : - (IBAction)push_num0:(id)sender{
[self.push_number:sender];
}
- (IBAction)push_num1:(id)sender{
[self.push_number:sender];
}
- (IBAction)push_num2:(id)sender{
[self.push_number:sender];
I am running into an issue in the CalcController.m file.
error: request for member 'push_num1' in something that not a structure or union
error: syntax error before ':' token
Clearly the self.push_number:sender line has a problem, and it has something to do with the 'push_number' member
Please help me translate machine language to English?
I'm so new at this, and I'm afraid I'm confused. Can you explain why my naming structure is erroneous.
Thank you kindly, good sir.
Ha ha ha, you wouldn't believe what was wrong! I spelt m_botton - m_buttom! It was staring me in the face all this time!
Thanks for your help, it works fine now.
Wow, this tutorial is great. I'm new to Xcode and have been trying to use Xcode 2 books with Xcode 3. I've been tripping over dumb little details in the interface (nothing in the Xcode 2 books matches what's on my screen and I couldn't find stuff.) I've gotten more from following along with your tutorials in an afternoon than in several days of staring at my books, getting frustrated with the app help pages, googling futilely and then giving up. Thank you!
What would need to be done to convert this to work on an iphone?
When i run the app i just crash before the GUI is visible what am I doing wrong?
There are no warnings or errors
Thanks a lot for this tutorial... its quite easy to understand for a beginner like me...
Thanks,
Varun :)
Look at the application i post
Due to my original file host going down, I currently do not have a mirror site that I can host my movie on (73 MB). If you would like to volunteer to host this file, I'd be glad to arrange a transfer, that way my readers can still view the part 3 video that accompanies the code for this final part in the series.
try splitting your video into more smaller parts so you can host it on youtube.
You could also write the tut out but i understand that it would take ages.
whatever you do I am looking forward to part 3 as i have just finished part 2 and am loving it
i'll host it! contact andee.sonne@gmail.com
Hello Chris, I just found your tutorial and I find it to be pretty amazing, I applaud you on being to concise and informative.
I have just one issue with the code, when I try to run it from Xcode, I get the error "Command /Developer/usr/bin/gcc-4.0 failed with exit code 1" Have you any idea what that means?
I figured I would ask you since I am pretty new to cocoa and Obj-C and it is hard enough to decipher your code.
Any feedback would be great, thanks for taking the time to make these tutorials, they are amazing! Keep up the good work!
I would like to see the third movie, but the mirror is down. Please sent me a note how I can help you with some webspace. r0ketrick at gmail dot com
I've updated the comment at the top to reflect a new download location. If you feel charitable and can provide additional space for my video somewhere, I'd be glad to add your spot to my mirror links.
Cheers,
Chris
Chris:
Not sure if you're still needing space, but I'm currently uploading the file I grabbed from sendspace to a location that has bandwidth to share.
you can find it at:
http://bsdeviant.org/mirror/CalcPart3.mp4
To my anonymous user, many thanks for the additional download mirror. I will add it to the list of mirrors.
Cheers,
Chris
when I try to run it from Xcode, I get the error "Command /Developer/usr/bin/gcc-4.0 failed with exit code 1" Have you any idea what that means?
There could be a number of reasons for the error. I pasted it directly into google and numerous hits pop up. I would start there.
Thanks,
Chris
Hey Chris,
Just wanted to say I really appreciate this tutorial; it was a good blend of XCode functionality and basic Objective C, which is great since it has been hard for me to find example code for Object C specifically. Keep it up!
Hi Chris,
Thanks for you Tutorial. It really helped me step by step to get my first Objective C++ application on Cocoa running and working.
I did not install the RegEx library, but have realized the call to the library with a workaround:
" ... [[calc_display stringValue] isMatchedByRegex:@"[.]"] ... "
was replaced in my code by a little trick by splitting the string..:
"... [[CalculatorDisplay componentsSeparatedByString:@"."] count] < 2) ...".
In my final application I had one point I did not get solved.
The title of my calculater remains "untitled" when running and I do not find where I can change this top window title: can anyone give me the right hint?
Regards,
Patrick
patrick.blogs@telenet.be
Good news,
The window title I mentioned in my previous post seems to be a compiler/linker problem related to the Xcode beta I was using.
I installed XCode 3.1.2 final version, and the problem is solved..
..
Gracias por darnos uno de los mejores tutoriales y de seguro te agregare como uno de mis favoritos en mi navegador!
Thanks for make this tutorial and that help us! and is fantastic your Bloog.
The walk-through was pretty good...i appreciate ur efforts!!!
Many thanks I found this very useful.
What I would like to know is how you made your background Black in console view this would really help me programming
Chris,
Hi. My name is V.Jenkins. I am an Apple/Mac Pro user (Print/Video Professional) who has, due to the economy, become extremely interested in studying, learning, and mastering X code- Cocoa frameworks, iPhone SDK, and Objective-C.
I have been watching your tutorials on building a basic calculator and I want to thank you for shedding light on using X code and interface builder to create working applications. I have planned on returning to school-I received a Bachelor's degree in Film & Video- to receive a certificate in iPhone SDK and your tutorial is an excellent primer for the course. I will tell everyone that has an interested in learning X code to watch your video because if it helped me, a total Neanderthal when it comes to programming /software development, it can help ANYONE!
Again, I sincerely thank you for the tutorial videos and the sample code. I would love to continue to learn from you and to stay in your network.
Sincerly,
Mr. V. Jenkins
Post a Comment