Building the Lettuce app

-

All of the files that we work with in this tutorial are available on the GitHub repository.

When I was making the Lettuce app, I knew that I needed a few features:

  • I wanted the UITableView cells to look similar, if not the same, as the Android app.
  • Easy sharing buttons for Twitter and Facebook.
  • The articles needed to be shown within a native UIWebView.
  • The main purpose of the app: fetch and parse article information from the Lettuce XML file (or for some sites, this may be an RSS feed). I actually cover this in the next post.

Custom UITableViewCells

A native UITableView is attractive, but often we want our cells to have an appearance that isn't provided by any of Apple's built-in styles. One of the easiest methods of having greater control over cell appearance is by subclassing the UITableViewCell.

Before we start on that, obviously we will need a UITableViewController and a UITableView. I created a Single View Application project called "Lettuce Tutorial". In the storyboard file, I deleted the UIViewController and added a UINavigationController, setting it as the initial view.

UITableViewController for Lettuce

If you're following along, right click on the folder for your project name in the file explorer and click New File... then Objective C class. Create a subclass of UITableViewController. You can delete the original View Controller files.

Back in the storyboard file, focus on the UITableViewController. Open the Identity Inspector (i.e., the third tab). Under "Custom Class," set the class to the name of your newly created UITableViewController class.

Focus on the actual Table View within your TableViewController. In the Attributes Inspector, scroll to the bottom and set the background of the View. If you do this right, the color of the top of your "Root View Controller" should change to the color you set. By the way, you can change the text "Root View Controller" by selecting the Navigation Item within your Table View and opening the Attributes Inspector. While the storyboard file is already open, we can start customizing the cell appearance.

I dragged the bottom of the UITableViewCell so it was 100px tall. Then in the Attributes Inspector I set it to "Subtitle".

Using the same process as with the UITableViewController, I created a custom class for my UITableViewCells. I set the Reuse Identifier (under the Attributes Inspector) as "HeadlineCell".

Now I wanted to make each cell surrounded by a bit of padding. The easiest way to do this is by implementing the setFrame function within the LettuceTableViewCell class.

LettuceTableViewCell.m:

@implementation LettuceTableViewCell

- (void)setFrame:(CGRect)frame {
    int insetX = 10;
    int insetY = 5;
    frame.origin.x += insetX;
    frame.size.width -= 2 * insetX;
    frame.origin.y += insetY;
    frame.size.height -= 2 * insetY;
    [super setFrame:frame];
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)awakeFromNib
{
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end

Now let's move on to the LettuceTableViewController class. We want the table to show headlines and author names from an array. So in the LettuceTableViewController.h file we declare a few NSArrays that we will need.

LettuceTableViewController.h:

#import <UIKit/UIKit.h>

@interface LettuceTableViewController : UITableViewController

@property (nonatomic, retain) NSArray *headlinesArray;
@property (nonatomic, retain) NSArray *linksArray;
@property (nonatomic, retain) NSArray *authorsArray;

@end

And in the LettuceTableViewController.m file we synthesize these NSArrays.

@interface LettuceTableViewController ()

@end

@implementation LettuceTableViewController

@synthesize headlinesArray, linksArray, authorsArray;

I also changed the file so that there is one section in my table. The number of rows is the number of elements in headlinesArray. Each cell should be populated with the corresponding information in the arrays.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1; // change this line to 1
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return self.headlinesArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HeadlineCell" forIndexPath:indexPath];
    
    cell.textLabel.text = [self.headlinesArray objectAtIndex:indexPath.row];
    cell.textLabel.numberOfLines = 5;
    cell.detailTextLabel.text = [self.authorsArray objectAtIndex:indexPath.row];
    return cell;
}

Now we just need to put some elements in the NSArrays. I created a refresh function, just in case you want to add a refresh button to your app and call the function separately. I call the function in the viewDidLoad function.

- (void) refresh{
    NSMutableArray *tempHeadlines = [[NSMutableArray alloc] init];
    NSMutableArray *tempLinks = [[NSMutableArray alloc] init];
    NSMutableArray *tempAuthors = [[NSMutableArray alloc] init];
    
    [tempHeadlines addObject:@"Sample headline"];
    [tempLinks addObject:@"http://www.thelettuce.org"];
    [tempAuthors addObject:@"Neel Somani"];
    
    self.headlinesArray = tempHeadlines;
    self.linksArray = tempLinks;
    self.authorsArray = tempAuthors;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self refresh];
}
Working custom UITableView

Go ahead and run the application. Ta-da!

So now what do we have to do?

  1. show an Alert View when a user clicks on a cell, giving options to:
    • share the article
    • view the article
  2. fetch article information and populate the arrays with actual info

When the user wants to view an article, the UINavigationController should present a UIWebView that has loaded the URL corresponding to the user's selection.

Handling cell selection

When the user clicks on a cell, we want to present an Alert View. First we need to create an int to track which cell was just clicked.

@synthesize headlinesArray, linksArray, authorsArray;
int selected;

Here is the implementation of the didSelectRowAtIndexPath function.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"What now?" message:@"You have a few options here." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
    [alert addButtonWithTitle:@"View"];
    [alert addButtonWithTitle:@"Tweet"];
    [alert addButtonWithTitle:@"Share on Facebook"];
    [alert addButtonWithTitle:@"Cancel"];
    [alert show];
    
    selected = (int)indexPath.row;
}

And the handler for the UIAlertView:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    SLComposeViewController *composer;
    if (buttonIndex == 0){
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString: [linksArray objectAtIndex:selected]]];
    } else if(buttonIndex == 1){
        composer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
    } else if(buttonIndex == 2){
        composer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
    }
    if (buttonIndex == 1 || buttonIndex == 2){
        [composer setInitialText:[headlinesArray objectAtIndex:selected]];
        [composer addURL:[NSURL URLWithString:[linksArray objectAtIndex:selected]]];
        [self presentViewController:composer animated:YES completion:nil];
    }
}

Now we need to make a few changes before our project will run.

In our LettuceViewController.h we need to add the UIAlertViewDelegate:

@interface LettuceTableViewController : UITableViewController<UIAlertViewDelegate>

And at the top of LettuceViewController.m we need to import our social media functionality:

#import <Social/Social.h>
#import "LettuceTableViewController.h"
Linked Frameworks and Libraries

But we're not done yet. You need to click on your project name in the file explorer and scroll down to Linked Frameworks and Libraries. Click the "+" symbol and add Social.framework.

Try running the app now. When you try to view an article, the app currently launches the URL in the built-in browser rather than in its own UIWebView. Our social sharing functions, however, are complete.

A UIWebView will need its own View Controller. So create a subclass of UIViewController. In the storyboard, add a View Controller and set it to this custom class. Drag in a Web View that takes up the entire space of the controller.

In LettuceViewController.h we make a couple of changes:

#import <UIKit/UIKit.h>

@interface LettuceViewController : UIViewController {
    @public
    NSString *url;
}
@property (strong, nonatomic) IBOutlet UIWebView *webView;

@end

Link the IBOutlet to your Web View by clicking on the File's Owner button for your UIViewController and dragging the circle in the Connections Inspector tab to your UIWebView.

We also update the viewDidLoad function in LettuceViewController.m.

@implementation LettuceViewController

@synthesize webView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSURL *urlObj = [NSURL URLWithString:url];
    NSURLRequest *request = [NSURLRequest requestWithURL:urlObj];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if ([data length] > 0 && error == nil) [webView loadRequest:request];
        else if (error != nil) NSLog(@"Error: %@", error);
    }];
}

urlObj is an NSURL with whatever URL we pass to the controller. We make a request and put it in a queue (if there is one). Then we use our Web View's loadRequest function if it is a success.

We also need to create a manual segue. This will allow us to programmatically switch to the Web View.

File's Owner icon

Ctrl + drag from the yellow File's Owner icon of your TableViewController to the ViewController. Select "push".

Icon for push segue

Focus on the icon for your newly created push segue. In the Attributes Inspector, give it an identifier. I used "toWebView".

Now you can access this segue in your code. We can update our didSelectRowAtIndexPath function in LettuceTableViewController.m to reflect this.

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    SLComposeViewController *composer;
    if (buttonIndex == 0){
        //[[UIApplication sharedApplication] openURL:[NSURL URLWithString: [linksArray objectAtIndex:selected]]];
        [self performSegueWithIdentifier:@"toWebView" sender:[linksArray objectAtIndex:selected]];
    } else if(buttonIndex == 1){
        composer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
    } else if(buttonIndex == 2){
        composer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
    }
    if (buttonIndex == 1 || buttonIndex == 2){
        [composer setInitialText:[headlinesArray objectAtIndex:selected]];
        [composer addURL:[NSURL URLWithString:[linksArray objectAtIndex:selected]]];
        [self presentViewController:composer animated:YES completion:nil];
    }
}

And before the segue is performed, we can use the prepareForSegue function to send our URL.

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if ([[segue identifier] isEqualToString:@"toWebView"])
    {
        LettuceViewController *viewController = [segue destinationViewController];
        viewController->url = sender;
    }
}

This means that we have to import LettuceViewController.

#import <Social/Social.h>
#import "LettuceViewController.h"
#import "LettuceTableViewController.h"

If you run the application and click "View article", our native Web View handles the URL. Done!

One last step: we need to fetch the XML from http://www.thelettuce.org/return.php, parse it, and populate our arrays. This post is getting a bit long however, so I'll cover how to do this in the next post.

Feedback is welcome as always!

Tags: uitableview uitableviewcell custom table lettuce ios iphone

See also: How to get MacTeX faster: Easily using BasicTeX

Back to all posts

Neel Somani

About the Author

Neel Somani, a student at the University of California, Berkeley, is the founder of Apptic LLC. In addition to computer science, he's interested in philosophy and entrepreneurship. You can follow him on LinkedIn and Twitter.

comments powered by Disqus