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 […] By Ernesto García.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Make a Simple Mac App on OS X 10.7 Tutorial: Part 2/3
35 mins
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:
-(ScaryBugDoc*)selectedBugDoc
{
NSInteger selectedRow = [self.bugsTableView selectedRow];
if( selectedRow >=0 && self.bugs.count > selectedRow )
{
ScaryBugDoc *selectedBug = [self.bugs objectAtIndex:selectedRow];
return selectedBug;
}
return nil;
}
-(void)setDetailInfo:(ScaryBugDoc*)doc
{
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:
-(ScaryBugDoc*)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:
-(void)setDetailInfo:(ScaryBugDoc*)doc
{
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.
-(void)loadView
{
[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.editable=YES;
self.bugRating.displayMode=EDStarRatingDisplayFull;
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.