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
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.
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!!