Cocoa – Creating View Controllers from Code

Starting with an Empty Application, you have a program that contains a single class: AppDelegate and no storyboard. How do you build an application from this? Ignoring main.m and other esoteric code, your application begins at application:didFinishLaunchingWithOptions. When this method is called, your application has an empty window ready for you to populate with interesting objects.

Continue reading Cocoa – Creating View Controllers from Code

Objective-C – Delegates and Protocols

The delegate design pattern creates a system wherein one object passes work to another object. The first object stores a reference to another object, it’s delegate, and calls methods in the delegate object.

To make this system work there are two problems to solve. First. the main class needs a reference to the delegate object. The second problem is the need for the delegate object to implement the methods the delegator object will invoke. Without this guarantee your program runs the risk of no working or crashing.

The first problem is easily solved by adding a property to the delegator that holds a reference to it’s delegate object.

The second problem is solved by declaring a protocol. A protocol declares methods and properties that an object will invoke in it’s delegate. Any class that will act as a delegate implements the protocol, and the methods the defined in the protocol.

When the compiler sees a class that implements a protocol, it checks that the methods declared in the protocol are defined in that class. If not you get a warning or an error.

Declare a protocol in the header file above of @interface with:

@protocol ProtcolName <Class>
-(void)aMethod;
@end

Declare a delegate property to hold an object that implements the protocol. The delegate property should be weak to avoid dependancies!

@property (weak, nonatomic) id <ProtcolName> delegate;

Use the id type here since the delegate object could be of any type.

 

Object-C – Singleton

Singleton is a design pattern that guarantees that only a single instance of a class exists at any time. Singleton classes are good for Models and Datasources. The singleton is a great partner with a UITableView as the datasource for the tableview.

In the header file declare a class method that will return the shared instance. The shared instance is the single instance of this class that will exist. This method is also responsible for creating the new instance the first time the method is invoked.

+(id)sharedManager;

Define this method in the implementation file.

+(id)sharedManager {
    static Things *sharedThings = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedThings = [[self alloc] init];
    });
    return sharedThings;
}

In the sharedManager method declare the variable sharedThings as static. A static variable remembers its value across invocations of a function. That is, the value is set the first time the function is called, and that value is “remembered” the next time the function is called. Normally you would expect that the variable would be forgotten on each subsequent invocation.

The dispatch_once guarantees that the block is contains is only executed once. In the example sharedThings should only be created once, the first time sharedManager is invoked.

The last step is to return the sharedThings instance.

Cocoa – UITableView

These are a  few notes on creating the UITableView. Understand at the time of writing this, I’m a total beginner, so take everything here with a grain salt, and an once of skepticism. The purpose of writing this is to settle subject in my own brain.

As part of the MVC pattern UITableView is a View, so a table view knows how to draw itself. A table view typically needs a view Controller to manage it. A table view always needs a data source. The data source supplies, the number of section, rows, and the content for each row. Typically a table view needs a Delegate. The Delegate handles events generated by the table view.

An instance of the UITableViewController class can handle the role of Controller, Data Source, and Delegate. The UITableViewController, as a view controller, manages a view and this view is always a UITableView.

Any class that manages a UITableView must be a UITableViewDelegate. The data source for the UITableView must be a UITableViewDataSource. These are often the same class, I’m guessing  they don’t have to be.

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

@end

This is the UITableView Protocol

To display the UITableView these requirements must be met.

UITableViewDataSource

This protocol requires two methods be implemented:

-(NSInteger)tableView:numberOfRowsInSection
-(UITableViewCell *)tableView:cellForRowAtIndexPath

These two methods tell the UITableView how many cells to work with, and give you the opportunity to populate and configure the UITableViewCells.

Giving the table view the number of rows in a section allows it to get data via the index of that row. Without having to contain the actual data. Essentially tableView:numberOfRowsInSection tells the table view how many cells you will need to work with. When comes time to display a cell, the table view calls on tableView:cellForRowAtIndexPath passing the index of the cell, this function returns the cell; and within this function you populate and configure the cell.

-(NSInteger)tableView:numberOfRowsInSection

This method is relatively simple. It returns the number of cells in the table view. If you were using an array you might implement it in this way:

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [array count];
}

– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

The cell For Row method is more complex. Here is where table cells are created, populated, and configured. The table creates cells only as needed. It only creates enough actual cells to fill the screen. As cells scroll up the page they are recycled and repopulated with new data, with the tableview keeping track of which index is currently visible. This process makes the system much more memory efficient, since the computer holds only a limited number of cells in memory. Imagine your iTunes library with 500, or 1000, or more songs. The table view is really only working with enough cells to fill the screen, maybe 10. If the computer to generate 1000 or more table cells, it would slow down, and become unresponsive. Using this system, the table view is recycling a limited number cells like an escalator, changing the content as the cell moves off the top of the view and reappears at the bottom of the view.

You might implement this method as:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *simpleTableIndenitfier = @"SimpleTableCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIndenitfier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIndenitfier];
    }
    
    cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
    return cell;
}

Here the dequeueReusableCellWithIdenifier asks for an available cell from the table, which is stored in cell. If cell is nil, no cell was available, so a new cell is generated. In this case the cell is generated using the UITableViewCell alloc. Then initialized with initWithStyle. Here we tell initWithStyle to use one of the default cell styles UITableViewCellStyleDefault.

– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

This method tells the table view how many sections you will using. Be sure to set this to at least 1. The default implementation of this methods is set 0.

Storyboard and outlets

To connect a tableview created in storyboard to it’s view controller’s outlets, Control drag from the tableView to the view controller and choose dataSource. Repeat the process for delegate. You want the Outlets dataSource and delegate to read ViewController.