How to Make a Simple Mac App on OS X 10.7 Tutorial: Part 1/3
This is a post by iOS Tutorial Team Member Ernesto García, a Mac and iOS developer founder of CocoaWithChurros. It’s a good time to be an iOS developer. Not only can you release your apps to both the iPhone and iPad App Stores, but you also have the foundational skills to become a Mac developer, […] 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 1/3
40 mins
Scary bugs pictures and sample data
At this point, the MasterViewController class is ready to receive a list of Bugs, but then again, you don’t have any data yet.
Before adding the data, we’re going to need some scary pictures! You can download these pictures from the How To Create A Simple iPhone App on iOS 5 Tutorial or find your own favorite scary bugs on the Internet :]
Once you’ve downloaded the files or gotten your own, drag them all into the root of your Project Navigator tree. When the popup appears, make sure “Copy items into destination group’s folder (if needed)” is checked, and click Add.
If you want to keep things more organized, you could create a Sub-group for the bugs pictures, and drag the files to that group.
Now, let’s finally create the sample data. Select AppDelegate.m and add the following line at the top of the file, just bellow the #include “MasterViewController.h” line
#import "ScaryBugDoc.h"
In order to create the sample data and pass it over to the MainViewController, do the following changes in applicationDidFinishLaunching method, just above the line “[self.window.contentView addSubview:self.masterViewController.view];”:
// Setup sample data
ScaryBugDoc *bug1 = [[ScaryBugDoc alloc] initWithTitle:@"Potato Bug" rating:4 thumbImage:[NSImage imageNamed:@"potatoBugThumb.jpg"] fullImage:[NSImage imageNamed:@"potatoBug.jpg"]];
ScaryBugDoc *bug2 = [[ScaryBugDoc alloc] initWithTitle:@"House Centipede" rating:3 thumbImage:[NSImage imageNamed:@"centipedeThumb.jpg"] fullImage:[NSImage imageNamed:@"centipede.jpg"]];
ScaryBugDoc *bug3 = [[ScaryBugDoc alloc] initWithTitle:@"Wolf Spider" rating:5 thumbImage:[NSImage imageNamed:@"wolfSpiderThumb.jpg"] fullImage:[NSImage imageNamed:@"wolfSpider.jpg"]];
ScaryBugDoc *bug4 = [[ScaryBugDoc alloc] initWithTitle:@"Lady Bug" rating:1 thumbImage:[NSImage imageNamed:@"ladybugThumb.jpg"] fullImage:[NSImage imageNamed:@"ladybug.jpg"]];
NSMutableArray *bugs = [NSMutableArray arrayWithObjects:bug1, bug2, bug3, bug4, nil];
self.masterViewController.bugs = bugs;
Here you just use the ScaryBugDoc initializer to create four sample bugs, passing in the title, rating, and images for each. You add them all to a NSMutableArray, and pass them over to your masterViewController using the bugs property.
And finally you have some data! Compile and run your app, and make sure all works well without errors.
We still don’t see anything in the user interface, but at this point the view controller has the data it needs, and we’re able to begin the work in the user interface to finally show your Scary Bugs List.
A Different Kind of Bug List
In order to display your Bug List, you need to set up the table view to get the list from your model.
In OSX, the table view control is called NSTableView. It’s similar to UITableView in that it displays lists of data, but one major difference is that in NSTableView each row can have multiple columns!
Just like UITableView, NSTableView has cells for each row. However, there have been some recent changes as to how these work:
- Before OSX 10.7 Lion, table view cells were a special class derived from NSCell class. They were not based on views, and it was responsibility of the coder to handle the drawing and even mouse events (gah!)
- From OSX 10.7 on, there’s a new type of table view – the View-Based table view. This table view works in a very similar way to UITableView. The cells are a special type of view (NSTableViewCell), and working with it is very similar to the way it works in iOS – which is much easier!
In this tutorial you are going to use the new View Based Table View. We’ll cover the basics here, but if you want to learn more about NSTableView, you can read the Table View Programming Guide which does a great job explaining how the table views work.
Before setting up the user interface, you need to make a minor change in the nib files of the project – disable Auto Layout. Auto Layout is a new feature introduced in OSX 10.7 Lion, aimed to handle automatically the resizing of the User Interface controls based on a series of rules defined by the programmer.
Auto Layout is beyond of the scope of this tutorial and makes the explanation of some things a bit confusing, so you are going to disable it. When Auto Layout is disabled, the autoresizing can be configured and behaves exactly the same as in iOS 5 projects.
Select MasterViewController.xib. When the Interface Builder interface opens, in the Utilities panel on the right side of the window, make sure that the “File Inspector” tab is selected (it’s the first one on the left in the tab bar). In the File Inspector Tab, uncheck “Use Auto Layout”.
After that, you need to repeat the same operation with the main window. Select MainMenu.xib and select the Window. Disable auto layout in the same way you did with the Master View.
Now we’re ready. Let’s set up your table view so it can handle displaying a list of ScaryBugDocs. In the Project Navigator, select MasterViewController.xib.
Now, in the Interface Builder view, select your table view. Be aware that the table view is embedded in a scroll view, so first time you click it, you will select the scroll view.
In order to select the table view, click on it a second time (not a double-click, but a second click a moment later). Another way to select it is to click directly on the table view on the “Objects” Panel on the right side.
Once you have it selected, the first thing you need to do is change the table view to “View based”, because Interface builder creates “Cell based” table views by default.
To change it, make sure that you have selected the “Attributes Inspector Tab” on the properties panel on the right of the screen. And then, in “Content Mode” select “View Based”. Your list does not need multiple columns, so change the Columns property to one.
In order to customize the list a little bit, check the property “Alternating Rows”, which will draw the rows in alternating white/blue colors, and uncheck the “Headers” property. This will remove the heading of the table, because you don’t need it for the tutorial.
After removing the extra columns, the remaining column may be narrower that the table view. In order to resize it, just click on the table column (by clicking three times on the table view, or by using the Objects panel on the right) and resize it to the table’s full width.
The next step is to configure the cell view that the table view will use. Your list needs to display the image of the Bug and its name. You need an image and a text field in your cell to show that information. Interface Builder has a preconfigured NSTableCellView that includes an image view and a text field, so you are going to use that one.
In the Object library panel on the bottom left side of the window, locate the “Image & Text Table Cell View”, and drop it on your table view.
After doing that, your table has now two different types of cells. Remove the old cell type (the cell that does not have the gear icon on the left) by clicking on it and pressing the Delete Key on your keyboard.
The last step is changing the height of the cell, because now it’s too small to show the bug image. you want to set its height to 32. Select the cell by clicking on it, and then open the “Size Inspector” tab in the Utilities panel on the right side of XCode window. You can change the height of the cell to 32 in the Height panel.
Another way to do it is dragging the bottom border of the cell until you get to the desired height. After that, the image and the text fields are a bit misaligned. To fix that, click on then and move them until they are centered in the cell. You can also resize the image view and play with the text field font to fit it to your needs.
Now the table view design should look like this:
Now you need to set the column identifier. This is a name you give to every column of the table view, so that when you want to perform some actions or you receive some notification from a column, you are able to identify the column.
This may not be strictly necessary in this tutorial, because you only have one column, but it’s a good practice to do it so that you don’t find any issues when you want to create a multicolumn table view in other projects.
To do that, select the table column (remember, you may need to click three times in the table view to select it, or you can use the Objects panel on the left), and after that, open “Identity Inspector” tab in the Utilities panel.
There, change the Identifier from “Automatic” to “BugColumn”.
That’s it for the table UI configuration. Now, you need to connect the table view with the MasterViewController, so that they are aware of the existence of each other.
Like in iOS, the table view has two properties that are used to let the table and its controller communicate: the datasource and the delegate.
Basically, the datasource is the class that will tell the tableview what data it needs to show.
And the delegate is the one that controls how the data is displayed, and the one receives notifications from the table view, like for instance, when a cell is selected.
Usually (but now always) the delegate and the datasource is the same controller. In this case, the datasource and the delegate will be your MasterViewController class.
This can be done programmatically, but for this tutorial you are going to do that connection in Interface Builder.
Select your table view, and in the Utilities Panel choose the “Connections Inspector” (the one with an arrow pointing to the right). There, in the “Outlets” section, you will see the delegate in the datasource. Let’s connect the delegate first.
To do that, click on the circle on the right of the delegate, and drag it to the File’s Owner (MasterViewController), located on the “PlaceHolders” panels on the left side.
Just with that you told the table view that its delegate is the MasterViewController. When you instantiate your view controller in your app, that connection will be automatically setup for us.
Now, repeat the same procedure for the datasource outlet. After doing it, you must see both connections pointing to the File’s Owner, like this.
That’s it, now we’re ready to add the necessary code in your view controller to show your Bugs List. Select MasterViewController.m and add the following at the top of the file, just below the #import “MasterViewController.h” line:
#import "ScaryBugDoc.h"
#import "ScaryBugData.h"
Then, paste the following code at the end of the file, just before the @end line:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get a new ViewCell
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
// Since this is a single-column table view, this would not be necessary.
// But it's a good practice to do it in order by remember it when a table is multicolumn.
if( [tableColumn.identifier isEqualToString:@"BugColumn"] )
{
ScaryBugDoc *bugDoc = [self.bugs objectAtIndex:row];
cellView.imageView.image = bugDoc.thumbImage;
cellView.textField.stringValue = bugDoc.data.title;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.bugs count];
}
Ok, let’s have a look at what we’re doing here. In order to show data in a table view, you need, to implement, at least two methods.
One is the dataSource’s numberOfRowsInTableView:. This is called by the OS to ask to the datasource (MasterViewController in this case) “How many rows do I need to show?” You respond by simply giving the list of bugs in your array.
With that method, the table view knows how many rows to display, but still does not know anything about which cells to display in every row, and what information those cells should have.
That is done in tableView:viewForTableColumn:row. This method will be called by the OS for every row and column of the table view, and there you have to create the proper cell, and fill it with the information you need.
If you have experience with iOS, you will find this is quite similar to the way UITableView works.
numberOfRowsInTableView: is the equivalent of numberOfRowsInSection: in iOS.
And viewForTableColumn:row is the same as cellForRowAtIndexPath: in iOS. The difference here is that in iOS you have to setup a cell based on its section and row, and in OSX the cell setup is based on row and column.
cellForRowAtIndexPath: is a very important method, so let’s look this method in detail:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get a new ViewCell
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
// Since this is a single-column table view, this would not be necessary.
// But it's a good practice to do it in order by remember it when a table is multicolumn.
if( [tableColumn.identifier isEqualToString:@"BugColumn"] )
{
ScaryBugDoc *bugDoc = [self.bugs objectAtIndex:row];
cellView.imageView.image = bugDoc.thumbImage;
cellView.textField.stringValue = bugDoc.data.title;
return cellView;
}
return cellView;
}
The first thing you do is getting a cellView by calling makeViewWithIdentifier:. This method will create (or reuse) the proper cell for a column based on the identifier (which is the one you setup in Interface Builder).
Once you have the cell, is time for us to fill it with information. Well have to do it differently depending of the column. That’s why you check for the “BugColumn” identifier. If the column identifier is “BugColumn”, then you set the image and text from the ScaryBugDoc.
In this case, this table only has one type of column so this check isn’t absolutely needed. However it’s good to know how to handle the multicolumn case, since you might need that for your own apps.
The last step is setting the cell information. Based on the row, you get from your bugs array the proper ScaryBugDoc, and then fill the image and the text field with the name of the bug.
That’s all you need to display information in a table view. It’s just a matter of defining the properties and connections in Interface Builder and implementing only two methods in your view controller.
Now, it’s time to compile and Run the application. If everything went fine, you should see the table view with all the Scary Bugs in your list!