Adding a UIImage above a TTLauncherView

I had a project recently where there needed to be an image at the top of the view with a TTLauncherView underneath it.

I struggled with this at first as I could see no mention of doing this in the Three20 documentation. In the end I had to actually create the UIImageView and add it manually to the view.

The first thing you have to do is declare a UIImageView in the header file.

@property(nonatomic, retain) UIImageView *logoImageView;

Then in the implementation file, synthesize the property.

@synthesize logoImageView;

Now we modify the loadView method to add the code needed to create and insert the logoImageView.

Allocate and initialise the logoImageView.

self.logoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(65, 0, 190, 108)];

I’ve used hard coded values to define the location and size of the UIImageView. I got these values by just drawing up a simple diagram and working it out based on the iPhones screen size.

Diagram of where UIImageView will be placed<br />

It would be much better to derive these values based on the views bounds.

Once the size / position of the UIImageView is done we go ahead and set the actual image file that we want displayed in the logoImageView

self.logoImageView.image = [UIImage imageNamed:@"logo_small.png"];

Now we need to modify the size of the Three20 launcherView so that it fits on the view properly now that we have a UIImageView being placed above it. We do this by modifying the line of code that creates the TTLauncherView.

launcherView = [[TTLauncherView alloc] initWithFrame:CGRectMake(0, 120, 320, 352)];

Previously we were setting the frame for the TTLauncherView to be the bounds of the view. Now we have to actually specify a manual frame size and position. I worked out these values from the diagram as well. Again, it would be better to derive these values based on the views bounds.

Once that is done, we just have to add the UIImageView to the view. Near the bottom of the loadView method is the code for adding the launcherView to the view

[self.view addSubview:launcherView];
[launcherView release];

Right before these lines, add some code to insert the UIImageView onto the view.

[self.view addSubview:logoImageView];
[logoImageView release];

Now you should be able to run your app and see the UIImageView above the TTLauncherView. I found on my first try that the TTLauncherItem buttons were being cut off because my logo was too big. I simply made the image smaller and recalculated the size / location to place the views.

Three20 has methods to modify the size of the TTLauncherItems, but I didn’t want to complicate things.

There’s a bunch of ways to make this better, but it works.

Using TTLauncherView

After my little rant about Three20 in a previous post I figured I should at least try to make things better for other people.

One of the first things I struggled with was getting the TTLauncherView working. I had followed the steps on the Three20 website to add the necessary files to the project but I wasn’t sure how I then got the TTLauncherView displaying on the screen.

So, to hopefully help someone else… here goes.

Start off in your AppDelegate.m file. It should have been created automatically for you when you created your project in XCode. First thing you need to do is actually include the Three20 header file.

#import "Three20/Three20.h"

I’m sure you could probably just import the TTLauncherView header file but I figured I’d just make it easier on myself and include the main Three20.h file.

After that, you need to setup the TTNavigator and the relevant view controller mappings. All this stuff needs to be done in the

-(void)applicationDidFinishLaunching:(UIApplication *)application

method.

Create the TTNavigator instance

TTNavigator *navigator = [TTNavigator navigator];

Set the navigator persistence mode

navigator.persistenceMode = TTNavigatorPersistenceModeNone;

My understanding of this is that Three20 actually keeps a record of where you are in the navigation while the app is running. This means when you close the app and reopen it, Three20 can start off from where you left it. In this case I am telling Three20 to not do that and instead the app will just start up as normal.

The next step is to setup the TTURLMap. Three20 has this neat feature where you can refer to view controllers using a URL. This actually makes working with view controllers a lot simpler, at least once you get the hang of it. NOTE: This also means you are pretty much locked into using Three20 once you start using TTURLs. If you get part way and decide you no longer want to use Three20 you are going to have to re-write everything.

Get a pointer to the navigators URL map.

TTURLMap *map = navigator.URLMap;

Use that pointer to setup all your URL mappings

[map from:@"*" toViewController:[TTWebController class]];
[map from:@"tt://launcher" toSharedViewController:[LauncherViewController class]];
[map from:@"tt://first" toViewController:[FirstViewController class]];
[map from:@"tt://second" toViewController:[SecondViewController class]];
[map from:@"tt://third" toViewController:[ThirdViewController class]];
etc
etc

After doing this you can then use the URL tt://launcher in your code to refer to your LauncherViewController (or whatever you have called it). I’ll give an example of this in a second.

Each of the ViewControllers referenced in the mapping code are standard UIViewController classes. You can actually map the URLs to any class.

After that you can actually get the launcher view controller to displaying using

[navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://launcher"]];

One line of code instead of the usual 3 or 4 thats regularly needed to push a view controller onto the navigation stack. You can see there how using the URL makes things a lot easier. It took me a while to get my head around this.

So when my app starts, it will run the applicationDidFinishLaunching method and setup the mappings and then get Three20 to launch the tt://launcher URL, which is mapped to the LauncherViewController class.

Now, on to the LauncherViewController file.

My LauncherViewController is a subclass of TTViewController. I believe it has to be in order to crate the launcher view.

In the LauncherViewController.m file we setup the Launcher View in the loadView method

First we allocate and initialise the launcherView using the views bounds. This means the launcher view will take up the whole screen.

launcherView = [[TTLauncherView alloc] initWithFrame:self.view.bounds];

Then we set the background color of the launcher view.

launcherView.backgroundColor = [UIColor whiteColor];

Then we set the number of columns we want

launcherView.columnCount = 1;

In this example I’m only having one column with 3 items. You can adjust this value at anytime and Three20 will automatically reposition the icons in your launcherView to fit.

The next part involves setting up the actual items that will be displayed in the launcherView. You do this by setting the pages property of the launcherView instance.

launcherView.pages = [NSArray arrayWithObjects:
                        [NSArray arrayWithObjects:
                      [self launcherItemWithTitle:@"First" image:@"bundle://firsticon.png" URL:@"tt://first"],
                      [self launcherItemWithTitle:@"Second" image:@"bundle://secondicon.png" URL:@"tt://second"],
                       [self launcherItemWithTitle:@"Third" image:@"bundle://thirdicon.png" URL:@"tt://third"],
                       nil], nil];

This creates our pages structure. It’s an array of arrays where the inner array represents the icons on the page and the outer array represents the page. So you could define a structure that has any number of pages and on each page you have a number of icons.

Next thing we have to do is set the launcherViews delegate. In this case I’m setting the LauncherViewController as the delegate.

launcherView.delegate = self;

Once that’s done, we can add the launcherView to the LauncherViewControllers view.

[self.view addSubview:launcherView];

Then release the launcherView instance

[launcherView release];

When creating the pages above, I used a separate method to make things less cluttered. The launcherItemWithTitle method looks as follows:

-(TTLauncherItem *)launcherItemWithTitle:(NSString *)pTitle image:(NSString *)image URL:(NSString *)url
{
    TTLauncherItem *launcherItem = [[TTLauncherItem alloc] initWithTitle:pTitle image:image URL:url canDelete:YES];
    return [launcherItem autorelease];
}

All this is doing is taking the items passed in an creating a TTLauncherItem from them.

With this done, all thats left to do is to implement the necessary delegate methods.

- (void)launcherView:(TTLauncherView*)launcher didSelectItem:(TTLauncherItem*)item
{
    [[TTNavigator navigator] openURLAction:[TTURLAction actionWithURLPath:item.URL]];
}

This method is called when an item is selected from the launcherView. It’s quite simple really. Using the mappings we created earlier we simply tell the TTNavigator instance to open the URL. We create the URLAction required using the URL we set when creating each TTLauncherItem.

What this means is that when an item is tapped in the launcherView we want it to load the relevant View Controller. This will push the relevant view controller onto the navigation stack and it will be displayed.

I think that pretty much covers a basic LauncherView implementation. It’s not really that hard once you understand the way Three20 handles navigation.

It’s a lot to get your head around especially if you are starting out in iOS development. Stick with it though.