Home Archive Tutorials

How to Make a Simple Mac App on OS X 10.7 Tutorial: Part 2/3

This is a post by iOS Tutorial Team Member Ernesto García, a Mac and iOS developer founder of CocoaWithChurros. This tutorial is the second part of a three part series on how to create a simple Mac App. In the first part of the series, you created a Mac application that showed a list of […]


  • Other, Other, Other

This is a post by iOS Tutorial Team Member Ernesto García, a Mac and iOS developer founder of CocoaWithChurros.

This tutorial is the second part of a three part series on how to create a simple Mac App.

In the first part of the series, you created a Mac application that showed a list of Scary Bugs.

In this second article, you’ll learn how to add a Details Section to show the complete information about a bug: its name, scariness rating, and a larger (and much more frightening!) picture of the bugs. (Jump to Part three)

You will also lean how to change that information, rate bugs, and even change their pictures!

So, let’s get started!

Download some stuff

In order to complete this tutorial, you’ll some pictures and some code files. So download this these extra project resources and uncompress it.

Note: In order to Rate the bugs from “not scary” to “totally scary”, you’re going use an open source rating control component called EDStarRating, which is included in the package.

In this tutorial we’re not going to explain how it’s built, but we’re going to teach you how to use a custom view like this one in your projects. The package also includes an NSImage category that you’ll use to create a thumbnail of the large Bug Image, and also three types of faces to show the rating.

For more information on EDStarRating, you can check out its github page.

Now, add those files to your project (as you did in the first part of the series, we’re going to create some groups to keep things organized):

  • Create a new group named “Views” in XCode, and drag EDStarRating.h/EDStarRating.m to that group, making sure “Copy items into destination group’s folder (if needed)” is checked, and that “ScaryBugsMac” is selected in the “Add to Targets” section. This is the 5-star rating view.

  • Repeat for NSImage+Extras.h/NSImage+Extras.m, except drag them to a new group named “Helpers”. This is some helper code we’ll need to resize images a bit later on. Again, make sure that all the required checks are selected (Copy Items check, and Add To Targets check).
  • Repeat for the three shocked face images, except drag them to a new group named “Art”. These are the images we’ll be using for the “stars” in our rating view for some humorous flair :] (Don’t forget about the required checks)

This is how your Project should look like after adding those files:

Creating the Details Section

Now it’s time to change our user interface to include the controls that will show the ScaryBugs detail information.

In iOS, a typical Master/Detail application creates two views, and when a row of the table is tapped, the interface changes to a different view to display the required information.

In OSX, the paradigm is different. Since you don’t have the size restrictions of the iPhone screen, you can add the detail information in the same view. you just need to resize it to be a bit bigger. It’s similar to the way iPad’s Master/Detail applications behave.

Let’s get to work. Open MasterViewController.xib. Select the view, and increase its height and its width. you don’t need to worry now about the exact size. Just big enough to fit in the new controls.
Something like this:

The next step is adding the required controls. You need to show the following detail information: the bug’s name, rating and image.

For the Bug Name you are going to use a NSTextField Control, which can show and edit text. For the Rating, you will use the EDStarRating control. And for the image, you will use an NSImageView.

Besides that, you will add two labels with the titles, just to let the user know what’s the meaning of every field.

To do this, you just need to do what you previously did with the table View. Find the required control in the Controls Panel at the bottom right of the screen, and drag them to our view.

Drag one Text Field (for the name), two Labels (for the titles), and one Image View (this one is named ‘Image Well’ in the controls panel).

The EDStarRating control is a custom control, so it’s not included in the panel. To use it, you need to find in the controls panel a control named “Custom View”. Drag it onto the main view. Later you will configure it so that it shows the rating.

Now it’s time to arrange the controls. Place them on the empty space on the right side of the view. Vertically, from top to bottom, arrange them this way:

  • First, a Label, which will be used as a title for the name. Below it, place the Text Field.
  • Below it, the second label (which will become the title for the rating).
  • Below that label, place the custom view (that will become later our rating control).
  • And finally, place the image view below the custom view

Try to arrange them so that the left edges of the controls are aligned. After that, your view should look similar to this:

The last step is to configure our view to auto-resize to match the size of its parent window. That way, you’ll be able to see the newly created controls just resizing the main window.

To do this, select the main view (by clicking on it or in the ‘Custom View’ in the objects panel on the left). Now, on the Utilities panel on the left, select the “Size Inspector” tab.

This is fifth tab, the one with a ruler icon. There, set up the autosizing attributes like the following:

Let’s configure the EDStarRating control. In the previous step, you added a custom view. This is a basic NSView. Now, you need to tell Interface Builder that you want the view class to be EDStarRating instead of NSView.

How do you do that? Quite easy. Click on the custom view to select it. In the Utilities panel, switch over to the Identity Inspector by clicking on the third tab.

In that tab, change the name of the Class to EDStarRating.

Now you need to change the text of the labels. Select the first label, and switch over to the Attributes inspector by clicking on the fourth tab. There, change the Title to ‘Name”.

Note you can also change the text of a label by double-clicking on it, and changing the text on the label directly.

Repeat that step for the second label, and change its title to “Rating”

Ok, now it’s time to build and run the application. If everything went well, the app should run and you should see a window similar to this:

Note: If part of your controls are cut off, you can open MainMenu.xib and make the window size bigger.

You can see that all the controls are there, but the Rating control is missing. Don’t worry about it. The control is there, but since you haven’t configured it yet it doesn’t show anything.

The view currently has all the controls you need to show the details. Now you need to add the outlets to the view controller so that you can later access those controls from our code.

To do this, still with MasterViewController.xib open, bring up the Assistant Editor (second button under the “Editor” section in the top toolbar), and make sure it’s set to Automatic\MasterViewController.m.

Select the table View (remember that you may need to click twice or just select the table in the Controls panel on the left side). When you’re sure that the table view and not the scroll view is selected, control-drag from the table view into MasterViewController.m, right after @interface MasterViewController ().

A popup will appear allowing you to hook the NSTableView up to a property in your class. Name it bugsTableView, make sure the Storage is set to Weak, and click Connect.

With that simple action, we’ve added a property to the view controller, and it’s internally hooked up to the table view in Interface Builder.

Now, you need to repeat those steps for the text Field and the image view (but not for the labels. we’re not going to change anything on the labels, so you don’t need to add those outlets).

You just need to do the same. Select the text field, and control-drag from it into MasterViewController.m, right below the new line with the table View NSTableView *bugsTableView. Call the property bugTitleView.

Repeat the same action for the image View, and call the property bugImageView. And the last one. Create the property for the rating view. Call the property bugRating.

After creating those properties, your MasterViewController.m file should look like this:

@interface MasterViewController ()

@property (weak) IBOutlet NSTableView *bugsTableView;
@property (weak) IBOutlet NSTextField *bugTitleView;
@property (weak) IBOutlet NSImageView *bugImageView;
@property (weak) IBOutlet EDStarRating *bugRating;


You will notice there is a warning since it can’t find the definition of EDStarRating – fix that by including the header at the top of the file:

#import "EDStarRating.h"

Showing the details

Now it’s time to show some information in those new controls you just added. When the user clicks on any row of our table view, you need to get the selected bug information, and show that information in the detail section.

This involves three steps. First, you need to know which row is selected. The table view tells its delegate that a row is selected by calling ‘tableViewSelectionDidChange’. So you need to implement that method in our view controller to receive that notification.

After that, you need to get from our bugs array the Scary Bug associated to the selected row. And the third step is to show the information of that bug in the details section.

Time to add some code. Select MasterViewController.m, and add the following code just before the numberOfRowsInTableView: line:

    NSInteger selectedRow = [self.bugsTableView selectedRow];
    if( selectedRow >=0 && self.bugs.count > selectedRow )
        ScaryBugDoc *selectedBug = [self.bugs objectAtIndex:selectedRow];
        return selectedBug;
    return nil;


    NSString    *title = @"";
    NSImage     *image = nil;
    float rating=0.0;
    if( doc != nil )
        title = doc.data.title;
        image = doc.fullImage;
        rating = doc.data.rating;
    [self.bugTitleView setStringValue:title];
    [self.bugImageView setImage:image];
    [self.bugRating setRating:rating];

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
    ScaryBugDoc *selectedDoc = [self selectedBugDoc];

    // Update info
    [self setDetailInfo:selectedDoc];    

You have three methods here. The first to mention is the tableViewSelectionDidChange method, which the OS will call when a row is selected in our table view.

Let’s have a look at this method in more detail:

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
    ScaryBugDoc *selectedDoc = [self selectedBugDoc];

    // Update info
    [self setDetailInfo:selectedDoc];    

In order to show the detail view, you need to perform two actions: First, get the selected Bug information. Second, you need to show that information in the details section. To keep things organized, you split that up into two methods – let’s cover those next!

Let’s take a look at the code in selectedBugDoc:

    NSInteger selectedRow = [self.bugsTableView selectedRow];
    if( selectedRow >=0 && self.bugs.count > selectedRow )
        ScaryBugDoc *selectedBug = [self.bugs objectAtIndex:selectedRow];
        return selectedBug;
    return nil;


The first thing you do is get the tableView’s property “selectedRow”. With that, you get the index of the selected row.

After some sanity checks, you just get the ScaryBugDoc at that position in our bugs array, by calling objectAtIndex: . Then the function returns the selected ScaryBugDoc.

An finally, the code in setDetailInfo:

    NSString    *title = @"";
    NSImage     *image = nil;
    float rating=0.0;
    if( doc != nil )
        title = doc.data.title;
        image = doc.fullImage;
        rating = doc.data.rating;
    [self.bugTitleView setStringValue:title];
    [self.bugImageView setImage:image];
    [self.bugRating setRating:rating];

This method is very simple. It just reads the title, the rating and the image from the ScaryBugDoc, and passes that information to the controls.

In order to change the Bug Title, you call the setStringValue method of bugTitleView. To change the image, you call setImage: on the bugImageView control. Then you set the rating by calling setRating: method onthe bugRating control.

That’s it; with that simple code you are able to show the details for the selected bug.

The last step you need to do is to configure the Rating control. You need to set some properties for it to work the way you need – and you need to do this before the view is first shown on the screen.

If you’re familiar with iOS, you know about the view controller’s life cycle. The view controller receives calls to viewWillLoad/viewDidLoad , and that’s where you usually configure your subviews.

In OSX, the NSViewController does not implement those methods. In OSX, out View Controller needs to override the method loadView. This is invoked when the view controller creates the view.

So you will override this method and add any initial configuration you may need. Note it’s important to call [super loadView] at the beginning of this method, or the view will not be created!

Add the following code just above the -(ScaryBugDoc*)selectedBugDoc line.

    [super loadView];
    self.bugRating.starImage = [NSImage imageNamed:@"star.png"];
    self.bugRating.starHighlightedImage = [NSImage imageNamed:@"shockedface2_full.png"];
    self.bugRating.starImage = [NSImage imageNamed:@"shockedface2_empty.png"];
    self.bugRating.maxRating = 5.0;
    self.bugRating.delegate = (id<EDStarRatingProtocol>) self;
    self.bugRating.horizontalMargin = 12;
    self.bugRating.rating= 0.0;

Now, compile and run the application. You should see the window with the funny faces in the rating control, because you’ve just configured it successfully.

Also, now if you click on any row, the information and the image of the selected bug appears in the details section!

Awesome, now it’s starting to look like a real app! :]

However there’s one small problem. The image is too small – and so it’s not scary enough! That’s because the image view isn’t set to scale the images up by default.

Let’s fix that. Select MasterViewController.xib. Now select the image view, and go to the Attributes Inspector (fourth tab in the Utilities panel). In that Tab, change the “Scaling” property to “Proportionally Up and Down”.

Compile and run the application again.

Now the bug picture is scaled and looks much better – and scarier! Well, except for that lady bug maybe.

At this point, you can select a bug in the list and see its details. You can also change the text and the rating in the details section. But after you make a change, none of that changes are reflected in the bug list and model!

Besides, you want to be able to add new bugs or delete existing bugs. So in the next section, you’re going to add those editing capabilities to your app!

Adding and deleting bugs

Now it’s time to implement the editing functionalities. First, you will learn how to add new rows to the list and delete selected bugs in the list.

First you need to add two buttons. One to add a new row, and another one to delete a selected row.
Find in the controls panel the “Gradient Button” and drag two of those buttons below your table view.

Select one of the buttons, and open the Attributes Inspector (fourth tab in the Utilities Panel). This will be the “Add row” button. For these buttons, we’re not going to set a title. you will use the system images instead.

In the attributes inspector, locate the Title Property, and delete all the text. It should be empty so that our button does not show any text. Now, in the Image property you are going to select the image with a “+” sign.

That image is named “NSAddTemplate”. You can type the name or you can click on the combobox and scroll down until you find it.

After that, repeat the same process with the other button. Delete its title, and set the image property to “NSRemoveTemplate”, which is the image with a “-” sign.

After switching the buttons to use the images, you might want to resize them so they’re a bit smaller.

Now you have two buttons, but they don’t do anything, because you haven’t hooked them up with the view controller.

You need to create an action for every button. This is done almost in the same way that you did when you created the properties for the controls, and is just like you would do in iOS.

Again, bring up the Assistant Editor (second button under the “Editor” section in the top toolbar), and make sure it’s set to Automatic\MasterViewController.m.

Select the “Add” button, and control-drag from the button into MasterViewController.m, at the end on the file, right before the line @end.

A popup will appear allowing you to create a new action for that button. Name the action addBug, and click Connect.

After that, a new method addBug: is created in our viewController. Every time the Add Bug button is clicked, the system will call that method.

Repeat the process for the delete button, and name the action deleteBug.

It’s time to add code in those methods to add and delete the bugs. Let’s begin with the code to add a new Bug. Add the following code inside the addBug: method

// 1. Create a new ScaryBugDoc object with a default name
ScaryBugDoc *newDoc = [[ScaryBugDoc alloc] initWithTitle:@"New Bug" rating:0.0 thumbImage:nil fullImage:nil];

// 2. Add the new bug object to our model (insert into the array)
[self.bugs addObject:newDoc];
NSInteger newRowIndex = self.bugs.count-1;

// 3. Insert new row in the table view
[self.bugsTableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:newRowIndex] withAnimation:NSTableViewAnimationEffectGap];

// 4. Select the new bug and scroll to make sure it's visible
[self.bugsTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:newRowIndex] byExtendingSelection:NO];
[self.bugsTableView scrollRowToVisible:newRowIndex];

Let’s see what we’re doing here.

First, you create a new ScaryBugDoc object. After that, you add it to your bugs array.

The last step is to insert a new row in the table view for that bug. After that, the OS will automatically call the method viewForTableColumn:row and the cell will be updated with the Bug information.

The last two lines are just cosmetic additions. You select the newly created row and you make the table view scroll to that row so that the newly created row is visible.

And now for the Delete button, paste the following code inside the deleteBug method:

	// 1. Get selected doc
ScaryBugDoc *selectedDoc = [self selectedBugDoc];
if (selectedDoc )
    // 2. Remove the bug from the model
    [self.bugs removeObject:selectedDoc];
    // 3. Remove the selected row from the table view.
    [self.bugsTableView removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:self.bugsTableView.selectedRow] withAnimation:NSTableViewAnimationSlideRight];
    // Clear detail info
    [self setDetailInfo:nil];

In this method you first call selectedDoc: you wrote earlier to get the currently selected bug – ah, the benefits of reusable code!

If you can find a selected bug (maybe there is no selection), you remove that bug from your array by calling the array method removeObject:.

That method will locate our bug doc object in the array, and will remove the object from it. Then, you remove that row from the tableView.

In the last line you just update the detail section with a nil value. That will actually clear all the information of the selected bug.

And that’s it – compile and run! If everything went fine, now when you click on the Add Bug button, a new line is added.

You can also delete a bug by selecting a row, and clicking the delete button.

Editing bug information

At this point, you can add and remove Scary Bugs from our list. Now is time to edit existing bugs.
You can make three changes to a bug: Change its name, change its rating and change its image.

First, let’s see how you can change the name. When you select a bug, it’s name is set in the text field located in the details section.

Right now you can change it, but the changes are not stored in the model, so those changes are lost. You need to update the model every time the user changes the text of the selected bug.

In order to achieve that, first you need to know when the text has been changed. You don’t want to receive a notification every time the user changes a character, but when the user has finished editing.

This happens when the user is typing and presses ENTER, or when he changes from the text field to other control. In this case, the text field sends an action when it happens, in the same way that the buttons send an action when is clicked.

So, the way to implement it is the same. Select MasterViewController.xib, bring up the Assistant Editor (second button under the “Editor” section in the top toolbar), and make sure it’s set to Automatic\MasterViewController.m.

Select the text field, and control-drag from it into MasterViewController.m right before addBug method:

A popup will appear allowing you to create a new action for that text field. Name the action bugTitleDidEndEdit:

That method will be called by the OS when the user finishes editing the text. Add the following code inside this method:

// 1. Get selected bug
ScaryBugDoc *selectedDoc = [self selectedBugDoc];
if (selectedDoc )
    // 2. Get the new name from the text field
    selectedDoc.data.title = [self.bugTitleView stringValue];
    // 3. Update the cell
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:[self.bugs indexOfObject:selectedDoc]];
    NSIndexSet * columnSet = [NSIndexSet indexSetWithIndex:0];
    [self.bugsTableView reloadDataForRowIndexes:indexSet columnIndexes:columnSet];

First, you get the selected bug by calling the selectedBugDoc method. Then, you get the new text from the text field, and change the bug title in the document.

The last step is to change the title in the table view. For that, you just tell the table view to reload the row for the current bug. This will cause viewForTableColumn to be called, which will then reload the table view cell appropriately.

Note: It is better to update a cell in by reloading it as shown here rather than trying to directly manipulate the cell’s content outside of viewForTableColumn.

Compile and run the application. Now, if you select a bug and edit its name (remember to press enter), the name will be changed in the table view!

If you change selection, and go back to it, the new text is still there, because now we’ve stored it in our model objects.

Now it’s time to change the rating. EDStarRating works in a similar way as the tableview does. You need to define its delegate, and the OS will call a method to inform us that the rating has changed.

We’ve already configured all that in the loadView method in a previous step, so you just need to add the method that will be called.

Select MasterViewController.m and add this code at the bottom of the file, just before the @end:

-(void)starsSelectionChanged:(EDStarRating*)control rating:(float)rating
    ScaryBugDoc *selectedDoc = [self selectedBugDoc];
    if( selectedDoc )
        selectedDoc.data.rating = self.bugRating.rating;

Here you are doing the same as before: you get the selected doc, and update it with the new value.

Compile and run the application. Now you can notice that the rating value is stored in the model and every time you change the rating for a Bug, that value is kept, even if you select other bugs and select that again later.

So, there is only one thing left to do – allow the user to change the bug’s image!

To do this, you’re going to add a new button. When the user taps it, you will present a window allowing the user to choose a new image.

To do that, select MasterViewController.xib. Find the “Push Button” control in the object library, and drag it to our view, just below the image view.

Change to button title to “Change Picture”:

Now, you need to add an action for it. Repeat the same steps you followed for the “Add” and “Delete” buttons, and name the action changePicture.

That action will be called every time you click the button. The next step is to show the user a window to change the bug’s picture.

For that, you are going to use an OSX specific control, called IKPictureTaker. This allows you to choose a picture from your computer or even use the webcam to take a picture, and just with a single line of code. You can learn more about this control in Apple’s ImageKit Programming Guide.

Once the user has selected the picture, the control will notify your view controller that a picture is available via a delegate callback.

Select MasterViewController.m and add these imports at the top of the file:

#import <Quartz/Quartz.h>
#import "NSImage+Extras.h"

Now, add this code inside the changePicture method:

ScaryBugDoc *selectedDoc = [self selectedBugDoc];
if( selectedDoc )
    [[IKPictureTaker pictureTaker] beginPictureTakerSheetForWindow:self.view.window withDelegate:self didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:) contextInfo:nil];

In this piece of code, you first check if you have a bug selected. If you find a bug, then you show the picture taker control so that the user can choose a picture.

With this single line of code, we’re showing a window to select the new image. In the same method you tell the picture taker control that our view controller (self) is going to be it’s delegate.

And that when it finished, it will call your delegate method pictureTakerDidEnd. In that method, you will collect the new image, and set it to the bug.

Add this code below the changePicture method, just above the deleteBug method:

- (void) pictureTakerDidEnd:(IKPictureTaker *) picker
                 returnCode:(NSInteger) code
                contextInfo:(void*) contextInfo
    NSImage *image = [picker outputImage];
    if( image !=nil && (code == NSOKButton) )
        [self.bugImageView setImage:image];
        ScaryBugDoc * selectedBugDoc = [self selectedBugDoc];
        if( selectedBugDoc )
            selectedBugDoc.fullImage = image;
            selectedBugDoc.thumbImage = [image imageByScalingAndCroppingForSize:CGSizeMake( 44, 44 )];
            NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:[self.bugs indexOfObject:selectedBugDoc]];
            NSIndexSet * columnSet = [NSIndexSet indexSetWithIndex:0];
            [self.bugsTableView reloadDataForRowIndexes:indexSet columnIndexes:columnSet];

So, when this is invoked, it means that the picture taker control has finished its work. But the user may have cancelled the operation, and you wouldn’t have any image available.

For that, you check that the control returned OK (NSOKButton) and that you have a new image available.

You should be familiar by now with the rest of the code. First you get the selected Bug, and then the cell in the table view for the selected row. The only difference here is that in the model and the cell you have to update the image.

Now, you can compile the application. But it won’t compile, because there are two errors.

Why is that? It’s because the IKPictureTaker control uses the Quartz framework, and that framework is not added by default to the applications. Let’s add it.

In the Project Navigator, select the Project (the root item of the tree). After that, select the ScaryBugsMac in Target sections in the view that appears.

And then, click on the “Summary” Tab. If you scroll down, you will see a list with the title “Linked Frameworks And Libraries”.

Click on the small “+” button that is located just below that list.

After that, a new window will popup for you to select the framework. Since there are a lot of them, is faster just to type “Quartz” in the search field.

In the filtered list, click on the Quartz.framework item, and then click on the Add button.

Now you can compile the project without errors and run the application.

If you select a bug and click on a button, you will be able to choose an image on your computer or even capture a new picture with your computer’s camera. That image will be associated to the selected bug.

So, up to now our application is almost ready. You can see the bug’s details, add or delete bugs, change their names, rate them and even change their pictures!!

Where to Go from Here?

Here is a sample project with all of the code we’ve developed so far in this tutorial series.

In the Final Part of the series, we’ll cover how to polish the application and take care of the small details that make an app look better and more professional.

Attributtion: Angry Bug Image is licenced under Attribution-Share Alike 3.0 Unported and it was created by the user FoeNyx.

This is a post by iOS Tutorial Team Member Ernesto García, a Mac and iOS developer founder of CocoaWithChurros.


More like this