View Images like Photos.app

Make sure you have JSON Framework installed first.

This is a continuation of Using threads for a JSON request

So right now we have four rows in our table. Each row has a image. So what if we want to view all the images at once. I really like viewing images at my own time. So I don’t want to use a timer and have them switch. If i want to skip a image I want to skip and if I want to take time to look, I want to look.

I really like how Apple has their Photos.app when you view images. You can slide your finger around to move to new images. So lets replicate this.

So open up your project and we need to create a new view to show our images. So right click on Resources -> Add -> New File. Then you want to create a user Interface and View XIB file. We want to call it picView.xib

So now we want to make a controller for the picView. So right click on Classes -> Add -> New File. And we want to create a new UIViewController subclass. We want to call it picViewController.m

Now we need to create a new a new outlet and a new method to init our view. So double click on picViewController.h and we want it to look like the following.

#import <UIKit/UIKit.h>

@interface picViewController : UIViewController {
	IBOutlet UIImageView *image;
}

- (id)initWithImage:(NSString *)url;

@end

Now we are creating a new init method. Normally we did a initWithNibName and then pass in the url to the image. Lets shake things up and and init our view with a custom method and load the nib in the custom function.

So open up picViewController.m and our initWithImage method will look like this

- (id)initWithImage:(NSString *)url {
    // loading the nib here
    if (self = [super initWithNibName:@"picView" bundle:nil]) {
		// grabbing the image and setting it to our imageview
		image.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: url]]];
    }
    return self;
}

Now save the file and double click on picView.xib and load up IB. So we want to add a imageview to our whole view so it looks like the following.

Then we need to change File Owner’s type to picViewController.

Now make the connection from the image outlet we created to the imageview on the view. Also make sure you connect the view to the File’s Owner.

Now that we have our picViewController all done lets create a nice button on the navigation bar for an option to show all images. So open up RootViewController.m and in the viewDidLoad method we add the following.

	UIBarButtonItem *showButton = [[[UIBarButtonItem alloc]
			initWithTitle:NSLocalizedString(@"Show All", @"")
			style:UIBarButtonItemStyleBordered
			target:self
			action:@selector(showAll)] autorelease];
	self.navigationItem.rightBarButtonItem = showButton;

Don’t worry we will create our showAll method later.

Now we need to create a new controller and view for the main view that will control the scrolling of our images.

So right click on Resources -> Add -> New File. Then you want to create a user Interface and View XIB file. We want to call it scrollView.xib

So now we want to make a controller for the scrollView. So right click on Classes -> Add -> New File. And we want to create a new UIViewController subclass. We want to call it scrollViewController.m

Now we need to create a new outlet for our scroll view. So open up scrollViewController.h and add in the following.

IBOutlet UIScrollView *scrollView;

Now save the file and double click on scrollView.xib. Now we want to create a new scroll view in the view. So the way to do this is drag a new UIView on the view that is already there and then change the type to a UIScrollView. It should look like the following.

Now we want to change the File Owner’s type to scrollViewController so it looks like the following

Then we want to create the outlet from the File Owner to the scroll view and then connect the view to the main view.

Now the one thing we want to make sure is our main view isn’t too high. If it is the user will be able to move the view up and down and that isn’t the desired effect. We only want them to flick right and left. 

So click on the View and you want to make the height 460. We will get rid of the navigation bar in code when this loads. If you want to keep the navigation bar you have to make the height even less.

Save the xib file and now lets get to some code.

So the first thing we want to is setup our picViewControntroller class. So open up picViewController.h and we want it to look like the following

@interface picViewController : UIViewController {
	IBOutlet UIImageView *image;
	UIImage *img;
}

@property (nonatomic, retain) UIImage *img;

- (id)initWithImage:(NSString *)url;

@end

So we have our image view outlet. We also have a UIImage that will contain the passed in image from our scroll view controller. We create a new init function to easily pass in the image on init. In that function we will call the init nib method

So now open up viewPicController.m. That whole file should look like this

@implementation picViewController

@synthesize img;

- (id)initWithImage:(NSString *)url {

	// loading the nib here
    if (self = [super initWithNibName:@"picView" bundle:nil]) {
		// grabbing the image and setting it to our imageview
		img = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: url]]];
    }
    return self;
}

- (void)viewDidLoad {
	image.image = 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 {
	[super dealloc];
}

@end

You can see on the new init, we load the nib and then set the passed in image to img. On our viewDidLoad we set the image to the imageview.

So our picViewController is now complete. Lets move on to the scrollViewController where most of the fun is happening.

So open up scrollViewController.h and make it look like the following. You should already have added he outlet for the scrollview

@interface scrollViewController : UIViewController  {
	IBOutlet UIScrollView *scrollView;
	NSMutableArray *viewControllers; // will contain all the picViewControllers
	NSMutableArray *jsonArray; // will get passed in a JSONarray
}

@property (nonatomic, retain) NSMutableArray *viewControllers;
@property (nonatomic, retain) NSMutableArray *jsonArray;

- (id)initWithJSON:(NSMutableArray *)array; // init method to get the json array
- (void)loadPage:(int)page;  // loads a new picViewController

@end

You will see we need to include a scrollview delegate also. This is so we can handle the movements of the scrollview.

Now save that and open up scrollViewController.m and we need to add our inits in. So yo will need to add the following.

- (id)initWithJSON:(NSMutableArray *)array {
	if (self = [super initWithNibName:@"scrollView" bundle:nil]) {
		// Initialization code
		self.jsonArray = [[NSMutableArray alloc] init];
		self.jsonArray = array;
	}
	return self;
}

Now we need to create a loadPage method. What this method is doing is it is saying load the picViewController into the viewControllers array if it is not already there. We will create this array a bit later so don’t worry about it not being there.

- (void)loadPage:(int)page {
    if (page < 0) return;
    if (page >= [self.jsonArray count]) return;

    // replace the placeholder if necessary
    picViewController *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null]) {

		// grabbing the image
		NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:page];

        controller = [[picViewController alloc] initWithImage:[itemAtIndex objectForKey:@"img"]];
        [viewControllers replaceObjectAtIndex:page withObject:controller];
        [controller release];
    }

    // add the controller's view to the scroll view
    if (nil == controller.view.superview) {
        CGRect frame = scrollView.frame;
        frame.origin.x = frame.size.width * page;
        frame.origin.y = 0;
        controller.view.frame = frame;
        [scrollView addSubview:controller.view];
    }
}

So you can see we are loading a new picViewController as long as it isn’t a page more then the the pics we have or less then 0. Then we add it to the scrollview at a set x/y.

So the idea is add page 0 first.. then add the next page to the width of page 0. Add page 2 the width of page 0 + width of page 1. That will give us the effect of scrolling back and forth

Now lets create our viewDidLoad method

- (void)viewDidLoad {
	[super viewDidLoad];

	// hiding the navigation bar
	self.navigationController.navigationBarHidden = YES;

    // view controllers are created lazily
    // in the meantime, load the array with placeholders which will be replaced on demand
    NSMutableArray *controllers = [[NSMutableArray alloc] init];
    for (unsigned i = 0; i < [self.jsonArray count]; i++) {
        [controllers addObject:[NSNull null]];
    }
    self.viewControllers = controllers;
    [controllers release];

    // a page is the width of the scroll view
    scrollView.pagingEnabled = YES;
    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [self.jsonArray count], scrollView.frame.size.height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.scrollsToTop = NO;
    scrollView.delegate = self;

    [self loadPage:0];
    [self loadPage:1];
}

So you can see up top I am hiding the navigation bar and loading a array full of nothing. There is a good reason for this. If you have a user that has say 150 images, you don’t want to load them all at once. It will kill the app. You want to load things as they are about to be shown. So at the botom we are loading the first page and the next page. This is so we don’t want the user to wait to see the image. We want it shown right away.

Now that is down we need our final method for the class. So now lets create a scrollViewDidScroll method. This is part of the UIScrollViewDelegate. This is how we can tell our application a user has scrolled. In our case it will let us know when a user has flicked a finger to the left or right and load the next set of views.

- (void)scrollViewDidScroll:(UIScrollView *)sender {
    // Switch the indicator when more than 50% of the previous/next page is visible
    CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;

    // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    [self loadPage:page - 1];
    [self loadPage:page];
    [self loadPage:page + 1];
}

So based on the current width we can tell what page we are on in the scrollview. Then based on that page load the previous page, current page and next page. Don’t worry if you are on page 0, our loadPage method won’t be called for a -1 page.

That is all there is to it. I wrote this over 2-3 days so things might be missing. I am sorry for that. You can grab the code here and read through it.

Using threads for a JSON request

Make sure you have JSON Framework installed first.

This is a continuation of Post JSON to a webservice

So if you are writting your webservice application and you have to wait a few seconds to make the request and get the response you will notice that your main application gets locked up. This is due to having to wait for the request to complete and nothing else can work till that request is completed.

So here is where we introduce threads. So the idea is this. We will click on a cell and load the view. It will be a blank view and then we will launch a thread to grab the information. Once we have that information the thread will complete and we can load the table.

So we will start off using the following source code. This is code from the UIImageView in a custom cell example.So for here we need to change our PHP JSON endpoint. We want to simulate the wait so we will just toss in a sleep(5); or such. So the endpoint now looks like this

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

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

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

);

sleep(5);
echo json_encode($array[$id]);

So lets change the endpoint in the code now. Open up JSONViewController.m and in the viewDidLoad method change the NSURL line to look like.

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

You can see the file use to be called test-json-get.php, I just made it test-json-get2.php. The new file has our sleep in it. If you click build and go now, you will notice that if you click on a cell to view it you will experience some delay and the UI is in a locked state till the 5 seconds are up. Once the 5 seconds are up the new view is displayed.

So our goal is to first load that new view and then display like a activity indicator in the upper right. Now you might be saying.. why use threads? Just display the indicator before making the JSON call. Well the reason is while we make the request is that the UI is locked and the indicator will stop so we want to do the request in a background thread so our main UI will continue to work.

So the first thing we want to do is move the JSON code from viewDidLoad into a new method. Lets call it getJSON. So here is what that function should look like

- (void)getJSON {
	NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get2.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"]]]];
	}
}

Your viewDidLoad should now be an empty method. So now lets put the activity indicator in there. So now the function should look like

- (void)viewDidLoad {
	CGRect frame = CGRectMake(0.0, 0.0, 25.0, 25.0);
	UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
	[loading startAnimating];
	[loading sizeToFit];
	loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
								UIViewAutoresizingFlexibleRightMargin |
								UIViewAutoresizingFlexibleTopMargin |
								UIViewAutoresizingFlexibleBottomMargin);

	// initing the bar button
	UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:loading];
	[loading release];
	loadingView.target = self;

	self.navigationItem.rightBarButtonItem = loadingView;
}

So you can see we are initing a new UIActivityIndicatorView and then creating a new bar button item and using our new indicator view as the bar item’s view. Then we are placing that item in the right hand side of the navigation bar.

So if we click build and go we will get something like this when we click on a cell.

So now we need to do the threading. So our JSON request got moved into a new function so that is where we will do the bulk of the request. So lets set that up.

At the top of the getJSON method you want to put the following code.

NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];

At the bottom of that method you want to put the following

[pool release];

So we are creating an auto release pool. Any items that we are set to autorelease in-between the two lines above won’t be released until we call [pool release]

So now we just need to init the thread. So at the bottom of viewDidLoad method add the following

[NSThread detachNewThreadSelector: @selector(getJSON) toTarget:self withObject:nil];

So that will call the method getJSON in a new thread. Now when you click build and go and click a cell and wait 5 seconds you will see the following.

You will notice that our indicator is still there, so we should remove that. So at the bottom of getJSON we want to remove it once the request is done. So add the following line

self.navigationItem.rightBarButtonItem = nil;

That is all you need for threads. Very simple huh? As always you can grab the code here.

Post JSON to a webservice

Make sure you have JSON Framework installed first.

This is a continuation of UIImageView in a custom cell

Here is a simple little code snippet on how to post some JSON to a webservice.

NSString *requestString = [NSString stringWithFormat:@"json=%@", [loginDict JSONFragment], nil];
NSData *requestData = [NSData dataWithBytes: [requestString UTF8String] length: [requestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: @"http://domain.com/api/login/"]];
[request setHTTPMethod: @"POST"];
[request setHTTPBody: requestData];

NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil ];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding: NSUTF8StringEncoding];

// decoding the json
NSDictionary *loginArray = [NSDictionary alloc];
loginArray = [returnString JSONValue];

So below is what each line does.

  • First we format the request string. What I am doing there is taking a whole dictionary and making it a JSON object. I then assign that to the json variable to post in. So in PHP land that would come in as $_POST['json']  and I could do like json_decode($_POST['json']) and turn it into an array I can work with in PHP
  • The next line turns the request into a data object so we know how big the string is.
  • Now we setup the URL to post the request to
  • We are setting the method to a post in html land it is like <form method=”POST”>
  • Now are are setting up the body of the post with our data
  • We now make the connection to the server and then get a data string back
  • We decode the data stream into a NSString object
  • Since you send in JSON I expect you get JSON back so we take the contents of the NSString and turn it into a NSDictionary

Pretty simple. That is all there is to it.

UIImageView in a custom cell

Make sure you have JSON Framework installed first.

This is a continuation of Click a Cell

So there has been some requests to do this. If you want to display a image in a custom cell it is pretty easy. It is about the same as doing the title and the URL to the image we did before. This will take over where click a cell left off. So you can grab the source code from there and start.

So we will be placing a UIImageView on the right part of the cell. To make it easy, we will still keep the title and the URL in the cell. So the first thing you want to do is open up ImageCell.h and we need to add the references to the new UIImageView we will use. I have commented the two lines I have added in this file

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

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

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

-(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;
@property (nonatomic, retain) UIImageView *imageView;  // added this

@end

Now we want to create a temporary image. When doing a webservice application it is key to “die” gracefully. For example if the service is alive put the image is not there, we want to show a no picture image so something is displayed. So I am using the following image (nopic-small.png).

Save this image and then drag it into your project so Xcode knows you are using this image.

Ok now lets init the UIImageView, so open up ImageCell.m and we need to synethsize our new image object.

@synthesize imageView;

Now look for the initWithFrame method. You want to add in the following

UIImage *noPicImageSmall = [[UIImage imageNamed:@"nopic-small.png"] retain];

self.imageView = [[UIImageView alloc] initWithImage:noPicImageSmall];
[myContentView addSubview:self.imageView];
[self.imageView release];

So the above code we are creating a new UIImage that contains the our no-pic image. Then we init our image view with the contents of our no-pic UIImage. We then add it to the sub-view and release the image.

So if we click build and go right now we should get something that looks like this

You will see the no-pic images are placed in the upper left corner of the cell and way too big, so lets draw them in the correct place and the correct size.

So no look for the layoutSubviews method and add in the following right below where we drew the titleLabel and urlLabel.

self.imageView.frame = CGRectMake(boundsX + 200, 5, 30, 30);

The boundsX + 200 is the spacing on the left
The 5 is the spacing from the top
The first 30 is the width of the view
The second 30 is the height of the view

You will notice in the line above that I switched from 2 lines of code to 1. This is actually better coding practices then above if you just need the CGRectMake once.

So if you click build and go now you will get something that looks like this

Now we need to populate that UIImageView with our image in the URL for the cell. We will do that in the setData method. So that should now look like

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

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

So you can see we added in the self.imageView.image = line. We are setting it to a new UIImage and loading it with data from the contents of a URL. You have to pass in a NSURL object to that, I don’t think it will accept a string. Pretty much you turn the string into a URL object and tell UIImage to grab that.

Don’t forget to dealloc the image view

- (void)dealloc {
	// make sure you free the memory
	[titleLabel dealloc];
	[urlLabel dealloc];
	[imageView dealloc];
	[super dealloc];
}

Now when you click build and go, you will see the following

You can grab the source here.

Click a Cell

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.

Next Page →