Click a Cell

August 25, 2008    JSON Programming

Make sure you have JSON Framework installed first.

This is a continuation of JSON Error

So here is a quick tutorial on how to click on a cell and load some JSON data for that cell. So the idea is you have a unique ID for each set of data you have.

So for that I am going to change the php code to add a ID

<php
header('Content-type: application/x-json');

$id = (int)$_GET['id'];

$array = array(
        1 => array(
                'id'  => 1,  // added this
                'title' => 'Brown Dog',
                'img'   => 'http://iphone.zcentric.com/files/1.jpg',
        ),
        array(
                'id'    => 2,  // added this
                'title' => 'Black Dog',
                'img'   => 'http://iphone.zcentric.com/files/2.jpg',
        ),
        array(
                'id'    => 3,  // added this
                'title' => 'Two Dogs',
                'img'   => 'http://iphone.zcentric.com/files/3.jpg',
        ),
        array(
                'id'    => 4,  // added this
                'title' => 'Girl',
                'img'   => 'http://iphone.zcentric.com/files/4.jpg',
        ),
);

echo json_encode($array);
?>

So now lets go back to the code. In ImageCell.h we want to add a new id for the cell that we will set in our setData function and also we want to create a getID function so we know what ID we clicked on.

So here is what the .h file should look something like now

#import 

@interface ImageCell : UITableViewCell {
    UILabel *titleLabel;
    UILabel *urlLabel;
    NSInteger *itemID; // added this
}

// these are the functions we will create in the .m file

-(void)setData:(NSDictionary *)dict;
-(NSInteger)getID; // added this

-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;

@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;
@property (nonatomic, assign) NSInteger *itemID; // added this

@end

Now open up the ImageCell.m and add the following line into the initWithFrame function

self.itemID = 0;

Now you have to synthesize that itemID

@synthesize itemID;

Now scroll down to the setData function and we need to set up the itemID with what we pass in from RootViewController. So it looks like this

-(void)setData:(NSDictionary *)dict {
    self.titleLabel.text = [dict objectForKey:@"title"];
    self.urlLabel.text = [dict objectForKey:@"img"];
    self.itemID = (int)[dict objectForKey:@"id"];
}

The reason we have to do the (int) in front of it is how objectForKey returns the data. We need to cast it into an int since we declared it as one.

Now lets create our getID function so add in the following

-(NSInteger)getID {
    return self.itemID;
}

So since we set the itemID to 0 in the init later in the code we can always do some error checking. So if it is 0 there must be a problem, else grab the data.

Now save the file and we need to create a new view. Follow the following entry for how to do that in IB. Just follow the IB sets, you can ignore the code, we will fill that in later. Also don’t forget to follow the step for creating a new ViewController!. Call the new XIB from IB JSONView and the new controller JSONViewController.m.

Now we need to create some outlets in IB.

So click on the File’s Owner icon and you can to click on the Controller Identity Tab in the Inspector. (Tools -> Identity Inspector)

Click the + icon for class Outlets and you want it to look like the following. For my example I dragged a UILabel and a UIImageView onto the main view so we need to tell the application that these will be usable variables in the code.

Now that we have the items on the view and the outlets set, we have to tell IB what the outlets will attach to. So open the Connections Inspector tab (Tools -> Connection Inspector) and you should see the new outlets there

They are jsonImage and jsonLabel. So now all you are doing is clicking on the circle to the right and dragging over to the UIImageView and releasing and that will setup the connection between the outlet and the item you placed. Do the same for the UILabel.

Now everything in IB is all setup, so lets get back to some code.

So we should already have the JSONViewController.h/.m file in place since we created the new view, so lets make it work. So open up JSONViewController.h and you want it to look like

#import 

@interface JSONViewController : UIViewController {
    IBOutlet UILabel *jsonLabel;
    IBOutlet UIImageView *jsonImage;
    NSDictionary *jsonItem;
    NSInteger *itemID;
}

-(void)setID:(NSInteger)val;

@property (nonatomic, retain) UILabel *jsonLabel;
@property (nonatomic, retain) UIImageView *jsonImage;
@property (nonatomic, retain) NSDictionary *jsonItem;
@property (nonatomic, assign) NSInteger *itemID;

@end

So you can see one new thing. The IBOutlet in front of the UIImage and UIImageView. These is basically there to tell IB that these are outlets. So everything else is nothing new. The one thing you will see is the method setID, we will use that to get in the ID of the clicked on cell. We need to know the ID of the data set we want to grab right?

Here is what my JSONViewController.m looks like

#import "JSONViewController.h"
#import <JSON/JSON.h>

@implementation JSONViewController

@synthesize jsonLabel, jsonImage, jsonItem, itemID;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Initialization code

        self.itemID = 0;
    }
    return self;
}

-(void)setID:(NSInteger)val {
    self.itemID = (NSInteger *)val;
}

/*
 Implement loadView if you want to create a view hierarchy programmatically
- (void)loadView {
}
 */

- (void)viewDidLoad {
    // init the url

    NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get.php?id=%@", self.itemID, nil]];

    NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];

    if (jsonData == nil) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later."  delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
        [alert release];
    }
    else {
        self.jsonItem = [jsonData JSONValue]; 

        // setting up the title
        self.jsonLabel.text = [self.jsonItem objectForKey:@"title"];

        // setting up the image now
        self.jsonImage.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [self.jsonItem objectForKey:@"img"]]]];
    }
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [jsonLabel dealloc];
    [jsonImage dealloc];
    [self.jsonItem dealloc];
    [super dealloc];
}

@end

Now you will notice a few things

  • I do not init the jsonLabel and jsonImage anywhere. IB does this all for me
  • You can see I uncommented the viewDidLoad method and did the JSON call in there. There are a few reasons for that. First we want to make sure the class is fully inited before making the call. We do this since we want to pass in the ID of the data set.
  • You can see how I am setting up the title and the imageview in the code.

Now save that file and open up RootViewController.m

We want to make the didSelectRowAtIndexPath method to look like

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

     // load the clicked cell
     ImageCell *cell = (ImageCell *)[tableView cellForRowAtIndexPath:indexPath];

     // init the controller
     JSONViewController *controller = [[JSONViewController alloc] initWithNibName:@"JSONView" bundle:nil];

     // set the ID and call JSON in the controller
     [controller setID:[cell getID]];

     // show the view
     [self.navigationController pushViewController:controller animated:YES];

}

The one odd thing is we are calling cellForRowAtIndexPath again when we click. This is because the cells are cached and we want to return that cell again if it already created.

That is all there is to it. If it works the view should look something like this if you click the same cell I did

As always you can grab the code here.



comments powered by Disqus