Download the source - 28.09 KB
Table of contents
- Table of contents
- Introduction
- Setting up the project
- The storyboard
- Coding the master view controller
- First run
- The segue
- Finishing the detail view
- Final run
Introduction
Let's use iOS 5, Xcode 4.2, and storyboards to make a simple Twitter app that will list tweets, and, when you click a tweet, show details about the tweet and user.
Setting up the project
In Xcode, start by creating a new project from File › New › New Project.
Select Master-Detail Application and click Next.
As Product Name enter 'Twitter Test'. Make sure the Use Storyboard and Use Automatic Reference Counting check boxes are checked. Click Next.
Select where to save the project and click Create.
Your project is now created. It has automatically set up a storyboard a master view controller and a detail view controller. The storyboard is where we will design our app – the controllers are where the programming takes place.
The storyboard
Let's open up our storyboard to see what it contains. Click on MainStoryboard.storyboard to the left and the storyboard comes up:
This basically tells us that we have:
- A navigation view controller – that's the type of controller that automatically gives a Back button when you navigate from a list view down to a detail view.
- A master view controller – that's where we'll have our tweet list.
- A the detail view controller – that's where we'll have details about each tweet.
The lines between each controller are called 'segues'. We'll talk more about these later on when we handle the action when the user selects a tweet.
Let's set up our table view.
Setting up the table view
Select the table view in the tree view to the left:
And to the right select Dynamic Prototypes:
This tells the table view that we're going to set up the cells dynamically from our controller.
Next, again in the tree view to the left, select the Table View Cell:
And, on the right, set style to Subtitle and Identifier to 'TweetCell' – that's the name we're going to use in our code to find the cell so we can fill out its details:
You'll see that the view style changes – that's the style we'll use to display the tweet text as the title, and tweet author as the subtitle:
So, now we're all set up in the storyboard part – at least as far as the master view goes. Don't worry – we'll get back to the detail view later on.
Moving on to the controller.
Coding the master view controller
Go into MasterViewController.m by selecting it on the left:
This is where all the action will take place. But first we need to set up an instance variable to contain our tweets.
Click on MasterViewController.h to the left:
And replace the @interface
to @end
part with the following code:
@interface MasterViewController : UITableViewController {
NSArray *tweets;
}
- (void)fetchTweets;
@end
This tells Objective-C that we have a tweets
instance variable and a fetchTweets
method.
Loading the JSON
Now to the actual tweet fetching. Go back into MasterViewController.m and insert the following method:
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: @"https://api.twitter.com/1/statuses/public_timeline.json"]];
NSError* error;
tweets = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
What this does is that it makes a separate thread to fetch the JSON data and then goes back to the main thread to update the table view when it's done. The reason to do this is that if we were to get the data using the main thread, then the application would lock up until the data was loaded.
Call this method from inside the viewDidLoad
method:
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchTweets];
}
This tells the application to load the JSON data as soon as the view loads.
So now our Twitter feed is loaded into to our instance variable named tweets
. This now contains an array holding a number of NSDictionary objects where NSDictionary is a key/value collection.
Filling the table view
Next, insert the following two methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:@"text"];
NSString *name = [[tweet objectForKey:@"user"] objectForKey:@"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:@"by %@", name];
return cell;
}
First run
Try running your application – click the Run icon:
And it works:
Pretty simple, huh? :-)
Well, can't dwell on our success, so let's go on to something that needs to be done.
The segue
Try selecting a tweet. Our intention was to load the detail view, but this doesn't happen. Why not?
Let's go back into the storyboard:
Notice how there's no segue between the master view controller and the detail view controller. What happened was that when we went from static cells to prototype cells in the storyboard before, it erased it, so now it doesn't know what to do. Let's help it out.
Setting up the segue
To the left, CTRL + drag from the Table View Cell to Detail View Controller, and select Push:
Now it knows what to do, so try hitting Run again.
And it works.
Click on the segue between the table view and detail view:
And change its identifier to 'showTweet':
That's how we're going to identify it later in the following code.
Responding to the segue
In MasterViewController.m, right below #import "MasterViewController.h"
, insert the following code:
#import "DetailViewController.h"
This is to give us access to the detail view controller from the master view controller.
Insert the following method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"showTweet"]) {
NSInteger row = [[self tableView].indexPathForSelectedRow row];
NSDictionary *tweet = [tweets objectAtIndex:row];
DetailViewController *detailController = segue.destinationViewController;
detailController.detailItem = tweet;
}
}
Basically what this does is that it tells the app what to do when it hits the segue between the master view and the detail view. It identifies the selected row, finds the tweet, and then sets the detailItem property on the detail view controller. (The detailItem property was automatically created when we selected Master-Detail Application in the beginning. Thanks Xcode!)
Now we're ready to do the detail view controller.
Finishing the detail view
Open up the storyboard and double click on the 'Master' and 'Detail' titles and change them to 'Tweets' and 'Tweet' accordingly:
Delete the label saying 'Detail view content goes here'. We'll be creating our own.
Insert a label for the name, a label for the tweet, and an image view for the profile image, like this:
You can customize the text sizes like you want.
Creating outlets for the detail view
We'll need a way to call these new controls from our code. This is done by creating three outlets which is a sort of wire, or connection, between the view and the controller. In DetailViewController.h, replace the @interface
line with the following code:
@interface DetailViewController : UIViewController {
IBOutlet UIImageView *profileImage;
IBOutlet UILabel *nameLabel;
IBOutlet UILabel *tweetLabel;
}
Now we have created the outlets. We now need to reference, or connect, these from our views. To the left, CTRL + drag from Detail View Controller to 'Label – Name goes here' and select nameLabel:
CTRL + drag also from Detail View Controller to 'Label – Tweet goes here' and select tweetLabel, and from Detail View Controller to 'Image View' and select profileImage.
Now we can call the labels and image view from our code.
Loading the detail view
In DetailViewController.m, replace the configureView method with the following code:
- (void)configureView
{
if (self.detailItem) {
NSDictionary *tweet = self.detailItem;
NSString *text = [[tweet objectForKey:@"user"] objectForKey:@"name"];
NSString *name = [tweet objectForKey:@"text"];
tweetLabel.lineBreakMode = UILineBreakModeWordWrap;
tweetLabel.numberOfLines = 0;
nameLabel.text = text;
tweetLabel.text = name;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imageUrl = [[tweet objectForKey:@"user"] objectForKey:@"profile_image_url"];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
dispatch_async(dispatch_get_main_queue(), ^{
profileImage.image = [UIImage imageWithData:data];
});
});
}
}
Final run
Try running the code, and there you have it – a list view and a detail view showing the tweet!
Hope you liked it :-)
Lasse is a long-time programmer, having more than 20 years of programming experience and more than 15 years of experience programming for the world wide web. He writes articles about Ruby on Rails, programming, SEO, and, recently, iOS programming.