Objective-C for the Absolute Beginner

We were all once beginners struggling, perhaps wandering aimlessly like I always have, around a new platform in a vain effort to make something work—even something as basic as a UITableView object on the iOS platform, part of Apple's Objective-C libraries.

Fear no more, and follow me on a wonderful adventure into the world of learning how to develop a basic iOS application for the iPhone or iPad. However, this guide assumes you do have some very limited knowledge of the syntax of C or related programming languages.

First, let's make a blank iOS application in Xcode. We'll call it "TableView Magic". I'm using Xcode version 5.0.2, so these screenshots may look slightly different from what you have, but you should be able to figure it out.

Launch Xcode and select "Create a new Xcode project". You'll be prompted to choose a type of project; Choose "iOS" from the left bar, then "Empty Application" from the field on the right. I don't recommend often using the pre-constructed applications Xcode gives you here, because they end up being more trouble than they're worth for customization, and you learn less with them.

You'll be asked to fill in some basic information for the application. For the product name, enter "TableView Magic", or whatever you'd like to call the app. You can pick an organization name of your own or use your personal name, and the Company Identifier follows a reverse-domain-name pattern. For example, Apple's identifier is com.apple, and any applications they make are identified as com.apple.applicationName, e.g. com.apple.mail or com.apple.terminal. The Class Prefix is whatever you want it to be—Squee! Apps uses SQU, and Apple uses NS for most of their classes. Thus, when I design a custom table view, it will be prefixed with SQU, like the SQUCustomTableView class.

Next, choose a place to save the project on your Mac. You can leave checked the box that says "Create git repository", but we won't be going into that now.

Now, you'll see that you have a new application with only the SQUAppDelegate class included.

The SQUAppDelegate.h file, and all files with the .h extension, are Objective-C header files in which you declare objects, methods, and properties, etc., of your classes. Thus, the SQUAppDelegate.m file and those with the .m extension are where you implement those items.

Now, let's create a new class called SQUViewController. Go to File>New>File, and you'll be prompted to select the template for your new file.

Choose Objective-C class and hit Next. You can name it anything you want, and this field should be filled in with your pre-designated Class Prefix (mine is SQU) already. Now, this will be a Subclass of UITableViewController. Objective-C grants you the powerful ability to subclass classes Apple has designed; that is, you can copy the default iOS objects and customize their properties and methods as you please.

Click Next and then Save the new class. There's no need to change the save location or targets in the final dialog, because Xcode already set the target as your current application and will save it along with the rest of the files.

Now you end up with two new files, one the header file, and the other the implementation file for your SQUViewController class.

Click on SQUAppDelegate.h and you'll find the following placeholder code:

#import <UIKit/UIKit.h>

@interface SQUAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Under the #import <UIKit/UIKit.h> line, add the code #import "SQUViewController.h". What you've just done is make a connection, between the new View Controller class you created, and the SQUAppDelegate. The SQUAppDelegate is the first file the compiler and the device looks at when dealing with your application, and it is the endgame for app management. And you've just provided it with a view to display to the user. Congrats!

Under the line @interface SQUAppDelegate : UIResponder <UIApplicationDelegate>, add this:

{
	UINavigationController *navigationController;
	SQUViewController *tableView;
}

This code declares that one of the objects under SQUAppDelegate is going to be of the class SQUViewController, which you can use because you have imported those files with the #import header, and that this object will be called tableView. We also declare an object called navigationController, an instance of the class UINavigationController. This can provide a navigation hierarchy and is tasked with managing view controllers that we can place inside of it, but we won't do much with it for now. You can now go to the SQUAppDelegate.m file and implement the app delegate to setup the view controllers.

In the SQUAppDelegate.m file, this boilerplate code should be at the top:

#import "SQUAppDelegate.h"

@implementation SQUAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
	// Override point for customization after application launch.
	self.window.backgroundColor = [UIColor whiteColor];
	[self.window makeKeyAndVisible];
	return YES;
}

@implementation implements your SQUAppDelegate class. The - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method is called when your application launches. So, accordingly, we'll put the view controllers' initialization code in here. Under the comment line // Override point for customization after application launch, add a return so the code's not too messy and type

tableView=[[SQUViewController alloc]initWithStyle:UITableViewStylePlain];
navigationController = [[UINavigationController alloc]init];

[navigationController addChildViewController:tableView]; //adds tableView as child of navigationController

self.window.rootViewController=navigationController;

This initializes the object you declared earlier, tableView, as an object of the SQUViewController class (which is a subclass of UITableViewController), and makes it a child view controller of the navigationController, essentially putting it inside of the navigationController.Then, it sets the application's window's rootViewController property, making the navigationController object the main view controller for the application.

All objects in objective-C are set to nil before they're allocated and initialized with the alloc and init messages. What is a "message" in this context? Objective-C is fancy in that you run methods on objects by sending them messages.

So, if I had a guy named Fred as an object in Objective-C as an instance of the class Human, I can create him, allocate and initialize him (fred *Human = [[Human alloc]init];), and then send him the message [fred goDriveACar];. I can also specify additional parameters: [fred goDriveACarWithBrand : carBrandFord ofModel : carModelExplorer];. Now Fred is a happily alive human driving a Ford Explorer.

Back to our iOS application. Now that we have set the root view controller of the application, we have to make it display something. Under the code you just typed, add the following:

UIBarButtonItem *leftButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:tableView action:@selector(addCell)];

UIBarButtonItem *rightButton = [[UIBarButtonItem alloc]initWithTitle:@"Count" style:UIBarButtonItemStylePlain target:tableView action:@selector(countCells)];

tableView.navigationItem.leftBarButtonItem = leftButton;
tableView.navigationItem.rightBarButtonItem = rightButton;

tableView.navigationItem.title = @"TableView Magic";

Let's go through it line-by-line.

Our navigationBar can hold two UIBarButtonItem objects, so we'll make them here. We'll call the left button leftButton and right button rightButton. Conveniently, we have some fancy initialization properties to set for these objects.

Altogether, the basic initialization method for the UIBarButtonItem is initWithTitle:style:target:action. initWithTitle: sets the title of the button, which is the text it displays. Confused about the @"Add Cell"? Well, in objective C, the @"" is shorthand for the creation of an on-the-fly NSString object, which is simply a string. You can pick one of Apple's declared styles for the bar with the style:UIBarButtonStylePlain is the default. Next, you can pick a target for the button, or what object will receive the message that the button has been clicked. We'll make that tableView, since that's the object we will be sending the message to. action: defines what message we will be receiving, and thus which instance method will execute. The @selector() calls a method for us, and that method will be addCell—we'll implement it later.

If you notice, leftButton has slightly different initialization code because we make use of one of the built-in system icons in iOS, the UIBarButtonSystemItemAdd, which is just a + icon.

Finally, we set the navigation items on the navigation controller and then give it a title to display in the center of the navigation bar.

Wander over to the SQUViewController.m file and we'll proceed to implement a table view

The UITableViewController, and its counterpart SQUViewController that we made manages a UITableView, and in the big picture that tableView is within the UINavigationController, which is within the application's window.

Unlike the UINavigationController class, which you can just immediately create objects from, UITableViewController MUST be subclassed in order to provide functionality. Thus, the boilerplate code in the SQUViewController.m file that Xcode provides you with is quite useful. These methods manage the cells in the tableView, how it looks, and how the user interacts with it.

Start by looking at the - (id)initWithStyle:(UITableViewStyle)style method implementation. When we call [[SQUViewController alloc]initWithStyle], the declaration here will override the method defined in the class UITableViewController written by Apple. So, this is where you can add // Custom initialization code, as Apple tells you.

Go to SQUViewController.h and under @interface SQUViewController : UITableViewController, type:

{
	NSMutableArray *dataSource;
}

This defines a NSMutableArray, or an array that we can edit (as opposed to an NSArray, which is static), and calls it dataSource. We'll put information in here to display on the table.

Return to SQUViewController.m and in the // Custom initialization section add:
dataSource = [[NSMutableArray alloc]initWithObjects:@"1", @"2", @"3", nil];

That means that our dataSource is now an array, initialized with the strings "1", "2", and "3" at indices 0, 1, and 2, respectively. The nil object tells the method that it's the end of the initWithObjects: list.

Now, there are three main protocols the UITableView subclass needs in order to work properly and display data for the user.

We need to implement these three methods at minimum:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {}

Since we want something to happen when somebody clicks a table view cell, we'll also implement -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {}.

For the numberOfSectionsInTableView: method, delete the code between the curly braces and type return 1;, since we will only need one section for this table view--more will be for more advanced work.

Now go to - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {} and make the content return [dataSource count];. The count method returns an NSInteger value of the number of items in the array; since we added those earlier, we know it's 3 right now, but it will be more later.

Now, for the most important part of initializing the table view: setting up the cells.

Find the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{} method, and make the content contained within the curly braces the following:

static NSString *cellID = @"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellID];
if(cell==nil){
    cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}

// Configure the cell...
cell.selectionStyle=UITableViewCellSelectionStyleNone;
cell.textLabel.text=[dataSource objectAtIndex:indexPath.row];

return cell;

On the line-by-line: First, we create a static string with the cell's identifier. Remember that the purpose of this method is to return the cell, each of them individually, for the view. It will run this method with a different indexPath.row until the numberOfRowsInSection: method (which returns the length of our sourceArray) is reached. So we have to configure the individual cell at the row passed to us in this method, which is indexPath.

We first must dequeue the cell identifier used in the last iteration; if the cell has been dequeued successfully (thus cell==nil evaluates to true), we allocate the new cell, then set up its view properties. Its selection style, or what change it undergoes when the user selects it, is set with the selectionStyle property. In this case, UITableViewCellSelectionStyleNone makes it so that the cell does not change color or style when it's tapped. Next, we set the text of the cell to the current row's index in the dataSource array. The method then returns our new cell!

You can build and run the application now and it should work. It should look like the picture below, but we're not quite done yet! If you have any bugs, look at the lldb compiler output in the Debug Area (View>Debug Area>Show Debug Area)---when you scroll to the top past the stack trace, there's more often than not an intelligible error message that you can deal with.

Now if you notice, when you click the buttons in the iOS simulator, the app crashes. That means we'd better implement the methods that are called when they're clicked. When we click the + button, we want to add the next counting number to the array, so while we have 1, 2, and 3 now, each time the user taps the + it adds 4, 5, 6, etc. When the user presses the Count button, we'll pop up an alert (a UIAlertView instance) with the number of items in the tableView array.

In SQUAppDelegate.m, we implemented each of the buttons as the following:

UIBarButtonItem *leftButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:tableView action:@selector(addCell)];

UIBarButtonItem *rightButton = [[UIBarButtonItem alloc]initWithTitle:@"Count" style:UIBarButtonItemStylePlain target:tableView action:@selector(countCells)];

The @selector declares the methods addCell and countCells targeted at the tableView, so we'll go to the SQUViewController to implement them. First, add the following to SQUViewController.h right before the @end argument to declare the methods:

-(void)countCells;
-(void)addCell;

Both of these functions will return no value, or void.

Now, in SQUViewController.m, we will implement the methods. It doesn't matter where we put them, so long as they're between the @implementation SQUViewController and @end arguments and not within any other method. We'll implement them as the following:

-(void)countCells
{
    UIAlertView *countAlert = [[UIAlertView alloc]initWithTitle:@"How many cells are there?" message:[NSString stringWithFormat:@"There are %i cells in this tableView!",[dataSource count]] delegate:self cancelButtonTitle:@"Okay!" otherButtonTitles:nil, nil];
    [countAlert show];
}
-(void)addCell
{
    [dataSource addObject: [NSString stringWithFormat:@"%i",[[dataSource objectAtIndex:[dataSource count]-1]intValue]+1]]; //add a new number string to the dataSource array
    
     NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[dataSource count]-1 inSection:0]; //create a new indexPath for the new row

    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; //refresh the data table by adding the new row with animation
}

So when the user taps the "Count" button, it calls the method countCells, which shows an alert with the number of rows in the tableView, which is reflected by the number of items in the dataSource array. We create the new alert and initialize it with initWithTitle: message: delegate: cancelButtonTitle: otherButtonTitles:, and nil is needed at the end to show that we're done with the method arguments. We set the title and message of the table view cell. In the line [NSString stringWithFormat:@"There are %i cells in this tableView!",[dataSource count]], we use a token in the NSString, %i, to add the integer value [dataSource count] into the middle of the text string. The cancelButtonTitle is the string for the cancel button for the user to dismiss the alertView, and we set the otherButtonTitles to nil because we only need the cancel button. Viola!

addCell adds a number to the table with animation for the newly-added row, so the interface doesn't look choppy and flashy as we make changes. First, we add a string to the dataSource, because all of the numbers in the array are NSString values and not int's. We create a new stringWithFormat, which allows us to add the %i token and thus cast the int as a string. This int value is the object at the last index of dataSource, i.e. [[dataSource objectAtIndex:[dataSource count]-1], cast from string to integer with the intValue method of NSString, then incremented by 1. Then we make a new NSIndexPath for the table view and add it to the view.

You can now build and run your application (Command+R is a handy keyboard shortcut). Everything should be working now.

Finally, we'll add one last method to the SQUViewController.m: one that responds when a cell is selected. Implement the following:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIAlertView *tappedCellAlert = [[UIAlertView alloc]initWithTitle:@"You tapped this cell!" message:[NSString stringWithFormat:@"This cell is cell number %i",indexPath.row+1] delegate:self cancelButtonTitle:@"Okay!" otherButtonTitles:nil, nil];
    [tappedCellAlert show];
}

Now, when the user clicks a cell, it calls this method and passes the indexPath parameter to the function. we can make an alert with the indexPath.row property to tell them which cell they clicked on, but remember that computers start counting at 0, and our tableView begins at the number 1, so we have to add 1 to accurately show which cell the user clicked.

If you have any questions about Objective-C or iOS programming, you can feel free to email me anytime or check out some of my other tutorials.