Reloading tableview after fetching asynchronous JSON data - iphone

I have used asynchronous JSON request and response to get some data. I want to populate it in a tableview. However, all I see is blank table cells. I tried doing a reloadData but it is not working. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://strong-earth-32.heroku.com/stores.aspx"]];//asynchronous call
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"%#", error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
SBJsonParser *parser = [[SBJsonParser alloc]init];
resultData = [[parser objectWithString:responseString error:nil]copy];
NSArray *storeArray = [[NSArray alloc]init];
storeArray= [resultData objectForKey:#"stores"];
populatedStoreArray = [[NSMutableArray alloc]init];
images = [[NSMutableArray alloc] init];
...........
[populatedStoreArray addObject:tempStore];
}
[self.storeDetailsTable reloadData];
[parser release];
}
The tableview looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
StoreCell *cell = (StoreCell *) [tableView dequeueReusableCellWithIdentifier:#"StoreCell"];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"StoreCellView" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (StoreCell *) currentObject;
break;
}
}
}
Store *storeAtIndex = [populatedStoreArray objectAtIndex:[indexPath row]];
cell.storePhoneNumber.text = [storeAtIndex phone];
cell.storeAddress.text = [storeAtIndex address];
cell.storeImage.image = [images objectAtIndex:[indexPath row]];
return cell;
}
Some more of my code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [populatedStoreArray count];
}
The header file:
#interface FirstViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *storeDetailsTable;
}
#property (nonatomic, retain) IBOutlet UITableView *storeDetailsTable;
#property (nonatomic, strong) NSDictionary *resultData;
#property (nonatomic, strong) NSMutableArray *populatedStoreArray;
#property (nonatomic, strong) NSMutableArray *images;
#property (nonatomic, strong) NSMutableData *responseData;
#end
Any pointers will be appreciated because everywhere I search, I'm told to use reloadData which is not working. In fact, xcode is not even prompting me when I use it.
Thank you.

Maybe you missed this line:
storeDetailsTable.delegate=self;

Related

prepareforSegue tableViewCell to different detailTableViews

I´m having a tableView in a viewController with an array (3 labelTitles to 3 different detailViews). My problem is in the prepareForSegue-method, I dont really know how to call the detailViews. I use the correct segue identifiers names.
"master".m:
#import "GuideTableViewController.h"
#import "GuideDetailTableViewController.h"
#import "GuideDetailTableViewController2.h"
#import "GuideDetailTableViewController3.h"
#import <QuartzCore/QuartzCore.h>
#import "customImageCell.h"
#interface GuideTableViewController (){
NSMutableData *weatherResponseData;
NSArray *titleLabels;
NSArray *imagesLeft;
}
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet UIImageView *imgHeader;
#property (weak, nonatomic) IBOutlet UIImageView *ImgTitle;
#property (weak, nonatomic) IBOutlet UIImageView *ImgWeather;
#property (weak, nonatomic) IBOutlet UIButton *btnMap;
#property (weak, nonatomic) IBOutlet UILabel *LabelWeather;
#property (weak, nonatomic) IBOutlet UILabel *LabelWeather2;
#end
#implementation GuideTableViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
//Weather method
- (void) loadWeather{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://api.wunderground.com/api/3919480da5014c98/conditions/q/BR/Sao_Sebastiao.json"]];
NSURLConnection *theConnection=[[NSURLConnection alloc]
initWithRequest:theRequest delegate:self];
if(theConnection){
weatherResponseData = [[NSMutableData alloc] init];
} else {
NSLog(#"failed");
}
}
//Delegates for WeatherData
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[weatherResponseData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[weatherResponseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSString *msg = [NSString stringWithFormat:#"Failed: %#", [error description]];
NSLog(#"%#",msg);
}
//All the data was loaded, let's see what we've got...
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:weatherResponseData options:NSJSONReadingMutableLeaves error:&myError];
NSArray *results = [res objectForKey:#"current_observation"];
NSString *cur = [results valueForKey:#"weather"];
NSString *tmp = [results valueForKey:#"temperature_string"];
NSString *wind = [results valueForKey:#"wind_string"];
NSLog(#"Current conditions: %#, %#º, %#", cur, tmp, wind);
self.LabelWeather.text = cur;
self.LabelWeather2.text = tmp;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self loadWeather];
titleLabels = [NSArray arrayWithObjects:#"Where to stay",#"Where to eat",#"What to do",nil];
imagesLeft = [NSArray arrayWithObjects:#"btn_Stay.png", #"btn_Eat.png", #"btn_Todo.png", nil];
//set background
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.jpg"]];
//rounded corners
[self.tableView.layer setCornerRadius:9.0];
[self.ImgWeather.layer setCornerRadius:9.0];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//TableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return titleLabels.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
customImageCell *cell = (customImageCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSString *cellLabel = [titleLabels objectAtIndex:indexPath.row];
cell.titleLabel.text = cellLabel;
NSString *cellImage = [imagesLeft objectAtIndex:indexPath.row];
UIImage *cellIcon = [UIImage imageNamed:cellImage];
cell.imageLeft.image = cellIcon;
return cell;
}
//To detailTableViewController
- (void)tableView:(UITableView *)tableView didselectRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == 0){
[self performSegueWithIdentifier:#"stay" sender:self];
}else if(indexPath.row ==1 ){
[self performSegueWithIdentifier:#"eat" sender:self];
}else{
[self performSegueWithIdentifier:#"todo" sender:self];
}
}
- (void) viewWillAppear:(BOOL)animated{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:self.tableView.indexPathForSelectedRow];
[cell setSelected:NO];
//Hide navbar
[self.navigationController setNavigationBarHidden:YES];
}
//Show navbar in detailView
-(void)viewWillDisappear:(BOOL)animated{
[self.navigationController setNavigationBarHidden:NO];
}
#end
change didselectRow
- (void)tableView:(UITableView *)tableView didselectRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == 0){
[self performSegueWithIdentifier:#"stay" sender:self];
}else if(indexPath.row ==1 ){
[self performSegueWithIdentifier:#"eat" sender:self];
}else{
[self performSegueWithIdentifier:#"todo" sender:self];
}
}

DidSelectRowAtIndexPath not being called

I have 3 different detailTableViews that my cells in my masterTableView will call when touched.
Please see my master.m file:
#import "GuideTableViewController.h"
#import "GuideDetailTableViewController.h"
#import "GuideDetailTableViewController2.h"
#import "GuideDetailTableViewController3.h"
#import <QuartzCore/QuartzCore.h>
#interface GuideTableViewController (){
NSMutableData *weatherResponseData;
NSArray *headGuide;
NSArray *leftImages;
}
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet UIImageView *imgHeader;
#property (weak, nonatomic) IBOutlet UIImageView *ImgTitle;
#property (weak, nonatomic) IBOutlet UIImageView *ImgWeather;
#property (weak, nonatomic) IBOutlet UIButton *btnMap;
#property (weak, nonatomic) IBOutlet UILabel *LabelWeather;
#property (weak, nonatomic) IBOutlet UILabel *LabelWeather2;
#end
#implementation GuideTableViewController
//Weather method
- (void) loadWeather{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://api.wunderground.com/api/3919480da5014c98/conditions/q/BR/Sao_Sebastiao .json"]];
NSURLConnection *theConnection=[[NSURLConnection alloc]
initWithRequest:theRequest delegate:self];
if(theConnection){
weatherResponseData = [[NSMutableData alloc] init];
} else {
NSLog(#"failed");
}
}
//Delegates for WeatherData
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[weatherResponseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[weatherResponseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSString *msg = [NSString stringWithFormat:#"Failed: %#", [error description]];
NSLog(#"%#",msg);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:weatherResponseData options:NSJSONReadingMutableLeaves error:&myError];
NSArray *results = [res objectForKey:#"current_observation"];
NSString *cur = [results valueForKey:#"weather"];
NSString *tmp = [results valueForKey:#"temperature_string"];
NSString *wind = [results valueForKey:#"wind_string"];
NSLog(#"Current conditions: %#, %#º, %#", cur, tmp, wind);
self.LabelWeather.text = cur;
self.LabelWeather2.text = tmp;
}
//JSONmethod
- (void) loadJSON{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//code
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://dl.dropbox.com/u/100670549/guide.json"]];
NSError *error;
if (data)
{
headGuide = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
for (NSDictionary *dictionary in headGuide){
// NSLog([dictionary description]);
}
}else
{
NSLog(#"Could not load data");
}
dispatch_sync(dispatch_get_main_queue(), ^{
// code
[self.tableView reloadData];
});
});
}
//Load
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self loadJSON];
[self loadWeather];
leftImages = [NSArray arrayWithObjects:#"btn_Stay.png", #"btn_Eat.png", #"btn_Todo.png", nil];
// set background
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.jpg"]];
// rounded corners
[self.tableView.layer setCornerRadius:9.0];
[self.ImgWeather.layer setCornerRadius:9.0];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return headGuide.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
NSArray *dict = [headGuide objectAtIndex:indexPath.row];
cell.textLabel.text = [dict valueForKey:#"title"];
NSString *cellImage = [leftImages objectAtIndex:indexPath.row];
UIImage *cellIcon = [UIImage imageNamed:cellImage];
cell.imageView.image = cellIcon;
return cell;
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"whereStay"]){
GuideDetailTableViewController *vc = [segue destinationViewController];
NSIndexPath *index = sender;
NSDictionary *dict = [headGuide objectAtIndex:index.row];
vc.stayGuide = dict;
}
else if ([segue.identifier isEqualToString:#"whereEat"]){
GuideDetailTableViewController2 *vc1 = [segue destinationViewController];
NSIndexPath *index = sender;
NSDictionary *dict = [headGuide objectAtIndex:index.row];
vc1.eatGuide = dict;
}
else if ([segue.identifier isEqualToString:#"whatTodo"]){
GuideDetailTableViewController3 *vc2 = [segue destinationViewController];
NSIndexPath *index = sender;
NSDictionary *dict = [headGuide objectAtIndex:index.row];
vc2.todoGuide = dict;
}
}
#pragma mark - tableView delegate
- (void)tableView:(UITableView *)tableView didselectRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == 0){
[self performSegueWithIdentifier:#"whereStay" sender:indexPath];
}else if(indexPath.row ==1 ){
[self performSegueWithIdentifier:#"whereEat" sender:indexPath];
}else{
[self performSegueWithIdentifier:#"whatTodo" sender:indexPath];
}
[tableView setAllowsSelection:YES];
}
#end
Your method signature is also not properly capitalized:
- (void)tableView:(UITableView *)tableView didselectRowAtIndexPath:(NSIndexPath *)indexPath{
should be
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Method names are case sensitive.
Additionally, make sure you're setting the tableView's delegate with
self.tableView.delegate = self;
It doesn't look like you're setting this object as the delegate of your table view. In your -viewDidLoad method, you should call [[self tableView] setDelegate:self];
Where is your protocol for tableview delegate and datasource?
#interface GuideTableViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
{
//Attributes...
IBOutlet UITableView *tableView;
}
in viewDidLoad, you should set the delegates:
tableView.delegate = self;
tableView.dataSource = self;
you could also set delegates in xib files...
So your delegates Methods should works... Check out with apple docs about tableview:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableView_Class/Reference/Reference.html

NSURLConnection and SBJSon in AppDelegate

I'm in xCode 4.2 using ARC and Storyboard (iOS 5). I suppose to put Tweets to my TableViewController. As I placed my NSURLConnection, everything goes fine and I have the response already in tweets(NSArray) in the delegate. The problem is it doesn't populate the table view. I am using SBJson for the received data. Am I missing something?
AppDelegate.m
#import "AppDelegate.h"
#import "TwitterViewController.h"
#import "SBJson.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize receivedData;
#synthesize tableViewController;
#synthesize tweets;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
tweets = [NSMutableArray array];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://search.twitter.com/search.json?q={username}&rpp=5"]];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
if (theConnection) {
receivedData = [NSMutableData data];
}
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSDictionary *results = [responseString JSONValue];
NSArray *allTweets = [results objectForKey:#"results"];
[tableViewController setTweets:allTweets];
}
#end
This is the delegate methods in my table view controller.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tweets count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"twitter";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *aTweet = [tweets objectAtIndex:[indexPath row]];
NSString *textFrom = [aTweet objectForKey:#"text"];
cell.textLabel.text = textFrom;
return cell;
}
I really need some advice.
Table reload. Gosh! I learned!

How to load table with some default image till it get the original image from internet iPhone?

I have a uitableview which stores one image in one cell. Its image get loaded from internet. The problem is the application get slow till its image get loaded from internet. So I want to put a mechanism that it first get loaded with a default image and when it gets the original then the default image will be replaced by the original one.
Give me any tutorial or any sample code for it.
Thanks in advance.
Get your image trough some other thread .... and use notification or delegate to keep track of any change in image download ... suppose you are using notification .... then in class which you set as observer for that notification reload the data of tableView .... so your table image will get updated where there was so default image .... for more detail lets take an example ..... Make a operation class to downloading the image we call it thumb .... In this example I make 2 classes
1. PhotoGalleryVC which shows a list of thumbs and some detail of each thumb and
2. LoadGalleryThumbOp [Op = operation] downloads thumbs and post notification when done
#protocol LoadGalleryThumbDelegate;
#interface LoadGalleryThumbOp : NSObject{
NSIndexPath* indexPathInTableView;
id <LoadGalleryThumbDelegate> delegate;
NSMutableData *activeDownload;
NSURLConnection *imageConnection;
NSString * documentPath;
BOOL imageDownload;
}
#property (nonatomic, assign) NSIndexPath* indexPathInTableView;
#property (nonatomic, retain) NSMutableData *activeDownload;
#property (nonatomic, retain) NSURLConnection *imageConnection;
#property (nonatomic, assign) id <LoadGalleryThumbDelegate> delegate;
#property (nonatomic, retain) NSString * documentPath;
#property (nonatomic) BOOL imageDownload;
- (void)startDownload;
- (void)cancelDownload;
- (void) persistData:(NSData*) data;
#end
#protocol LoadGalleryThumbDelegate
- (void)appImageDidLoad:(NSIndexPath *)indexPath;
#end
in LoadGalleryThumbOp.m do it as
#implementation LoadGalleryThumbOp
#synthesize year;
#synthesize indexPathInTableView;
#synthesize delegate;
#synthesize activeDownload;
#synthesize imageConnection,documentPath,imageDownload;
#pragma mark
- (void)startDownload
{
self.imageDownload = YES;
self.activeDownload = [NSMutableData data];
NSFileManager* fm = [NSFileManager defaultManager];
NSString* galleryDocumentPath = [self.documentPath stringByAppendingPathComponent:[NSString stringWithFormat:#"images/thumb.jpg"]];
if ([fm fileExistsAtPath:galleryDocumentPath])
{
UIImage *image = [[UIImage alloc] initWithContentsOfFile:galleryDocumentPath ];
self.gallery.thumpImage = image;
self.activeDownload = nil;
[image release];
self.imageConnection = nil;
[delegate appImageDidLoad:self.indexPathInTableView];
}
else {
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:#error yourImageUrl]] delegate:self];
self.imageConnection = conn;
[conn release];
}
}
- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.activeDownload = nil;
}
#pragma mark -
#pragma mark Download support (NSURLConnectionDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.activeDownload = nil;
self.imageConnection = nil;
self.imageDownload = NO;
[NSString stringWithFormat:#"images/thumb.jpg"]];
[delegate appImageDidLoad:self.indexPathInTableView];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self persistData:self.activeDownload];
self.activeDownload = nil;
self.imageConnection = nil;
[delegate appImageDidLoad:self.indexPathInTableView];
}
- (void) persistData:(NSData*) data
{
NSFileManager* fm = [NSFileManager defaultManager];
NSString* galleryDocumentPath = [self.documentPath stringByAppendingPathComponent:[NSString stringWithFormat:#"images/thumb.jpg"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:galleryDocumentPath])
{
NSError* err = nil;
[fm removeItemAtPath:galleryDocumentPath error:&err];
if (err)
NSLog(#"%s:%#",__FUNCTION__,err);
}
[fm createFileAtPath:galleryDocumentPath contents:data attributes:nil];
}
this class will download the image you wanted and will call its delegate when the image is download ..
Now comes its use part in PhotoGalleryVC use it like this
#interface PhotoGalleryVC : UIViewController <LoadGalleryThumbDelegate>{
IBOutlet UITableView* albumListTableView;
NSMutableDictionary *imageDownloadsInProgress;
NSArray* allThumbs;
}
#property (nonatomic, retain) NSMutableDictionary *imageDownloadsInProgress;
- (void)appImageDidLoad:(NSIndexPath *)indexPath;
#end
in .m part
- (void)viewDidLoad {
self.imageDownloadsInProgress = [NSMutableDictionary dictionary];
.....
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSFileManager* fm = [NSFileManager defaultManager];
NSString* galleryDocumentPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"Gallery/%#/images/thumb.jpg",[someObj.title stringByReplacingOccurrencesOfString:#" " withString:#"_" ]]];
//this above line is just make a seperate folder for each object and store thumbs of that object in that folder ... so make it unique some how
if (![fm fileExistsAtPath:galleryDocumentPath])
{
LoadGalleryThumbOp *galleryThumbDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (galleryThumbDownloader != nil && galleryThumbDownloader.imageDownload == NO)
{
[cell.activityIndicator stopAnimating];
cell.albumCoverImageView.image = [UIImage imageNamed:#"no_thumb.png"];
}
else {
[cell.activityIndicator startAnimating];
}
[self startIconDownload:temp forIndexPath:indexPath andYear:[NSString stringWithFormat:#"%d",selectedYear]];
}
else
{
[cell.activityIndicator stopAnimating];
cell.albumCoverImageView.image = [UIImage imageWithContentsOfFile:galleryDocumentPath];
}
return cell ;
}
//The following method see if there is already so downloader that is downloading same image then it simply do nothing else it create a downloader and start it
- (void)startIconDownload:(Gallery *)gallery forIndexPath:(NSIndexPath *)indexPath andYear:(NSString*)yr
{
LoadGalleryThumbOp *galleryThumbDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (galleryThumbDownloader == nil)
{
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* nameWithoutSpace = [gallery.title stringByReplacingOccurrencesOfString:#" " withString:#"_" ];
NSString* galleryDocumentPath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"Gallery//%#",nameWithoutSpace]];
galleryThumbDownloader = [[LoadGalleryThumbOp alloc] init];
galleryThumbDownloader.documentPath = galleryDocumentPath;
galleryThumbDownloader.indexPathInTableView = indexPath;
galleryThumbDownloader.delegate = self;
[imageDownloadsInProgress setObject:galleryThumbDownloader forKey:indexPath];
[galleryThumbDownloader startDownload];
[galleryThumbDownloader release];
}
else if(galleryThumbDownloader.imageDownload == NO)
{
if (albumListTableView.dragging || albumListTableView.decelerating) {
[galleryThumbDownloader startDownload];
}
}
}
finally the method which is called when a particular image is downloaded
- (void)appImageDidLoad:(NSIndexPath *)indexPath
{
CustomCellPhotoGalary* cell = (CustomCellPhotoGalary*)[albumListTableView cellForRowAtIndexPath:indexPath];
LoadGalleryThumbOp *galleryThumbDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (galleryThumbDownloader != nil)
{
if (galleryThumbDownloader.imageDownload)
{
[cell.activityIndicator stopAnimating];
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* nameWithoutSpace = [temp.title stringByReplacingOccurrencesOfString:#" " withString:#"_" ];
NSString* galleryDocumentPath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"Gallery/%#",nameWithoutSpace]];
cell.albumCoverImageView.image = [UIImage imageWithContentsOfFile:galleryDocumentPath];
//galleryThumbDownloader = nil;
}
else {
[cell.activityIndicator stopAnimating];
cell.albumCoverImageView.image = [UIImage imageNamed:#"no_thumb.png"];
[self startIconDownload:temp forIndexPath:indexPath andYear:[NSString stringWithFormat:#"%d",selectedYear]];
}
[albumListTableView reloadData];
}
}
woooh.....! thats a lot of code for one example
Note : I cut out so many line so this code may not work for u directly {there may be so many errors .. }, But i hope you get the main idea behind the scene ... :)
This sample code is great, It taught me a lot.
This uses the ASIHttpRequest framework, if you are going to use this technique, I recommend you use the latest version from here, because the version in the sample code is quite old now.
This sample code should help you with custom delegation of your cells etc.
As per above scenario, its always better to use LazyTableImages.
The best example given here

JSON data in NSMutable Array is out of scope and tableView won't display the cellData

So i'm working on an iphone app that parses json and tries to put the results into a plain UITableview. I have my tableview setup in the xib file and also setup the datasource and the delegate as the MainViewController (this is utility application). I get the JSON request just fine and I always test this by outputting it to a text label. But when I debug and hover over the cellData var (which holds this data), it shows out of scope, and my simulator shows an empty table that won't scroll...like it's stuck waiting for something to finish.
Here is my header for the view controller:
#import "FlipsideViewController.h"
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate, UITableViewDelegate, UITableViewDataSource> {
IBOutlet UILabel *label;
UITableView *theTableView;
NSMutableArray *cellData;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (retain) NSMutableArray *cellData;
- (IBAction)showInfo:(id)sender;
#end
and here is my implementation:
#import "MainViewController.h"
#import "JSON.h"
#implementation MainViewController
#synthesize label;
#synthesize cellData;
//Begin code for Table View Data Source (required methods)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//how many rows in the section; for testing purposes...placed here
return [cellData count];
//return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create a cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
//fill it with contents
cell.textLabel.text = [cellData objectAtIndex:indexPath.row];
//return it
return cell;
}
//End code for Table View Data Source
//Begin code for JSON stuffs
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
//TODO: need to extract this code out to a function because need to make multiple requests per run of the app....
//responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"https://tabular.heroku.com/tab_stores/tester.json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
cellData = [[NSMutableArray alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSMutableData *mutData = [[NSMutableData alloc] init];
[mutData appendData:data];
NSString *responseString = [[NSString alloc] initWithData:mutData encoding:NSUTF8StringEncoding];
NSArray *responseData = [responseString JSONValue];
NSMutableString *text = [NSMutableString stringWithString:#"T:"];
for (int i = 0; i < [responseData count]; i++)
[text appendFormat:#"%#\n", [responseData objectAtIndex:i]];
label.text = text;
NSLog(#"%#\n", cellData);
//Let's parse the data the way its supposed to be
//for every window, there should be a section.
for (int i=0; i<[responseData count]; i++) {
//manage table section
//maybe print window 1
}
//[cellData addObject:[responseData objectAtIndex:0]];
[cellData addObjectsFromArray:responseData];
// [responseData release];
NSLog(#"%#\n", cellData);
[theTableView reloadData];
[responseString release];
[mutData release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
label.text = [NSString stringWithFormat:#"Connection failed: %#", [error description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
//NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
//[responseData release];
//cellData = [responseString JSONValue];
//NSLog(#"this is in tabs %#", tabs);
// NSLog(#"this is in cellData %#", cellData);
// NSLog(#"this is in cellDatas %#", cellDatas);
//NSMutableString *text = [NSMutableString stringWithString:#"T:"];
// for (int i = 0; i < [cellDatas count]; i++)
// [text appendFormat:#"%#\n", [cellDatas objectAtIndex:i]];
// label.text = text;
//[theTableView reloadData];
}
//End code for JSON stuffs
.
.
.
- (void)dealloc
{
[theTableView release];
//[cellData release];
[super dealloc];
}
I know that the tableview calls those functions for number of sectionsiInTableView and numberOfRowsInSection first so it won't get the data, but then I thought calling reloadData would work...but nothing. Can anyone help a newbie out?
*NSLogs in console:
2010-08-15 15:42:07.421 Tabular[33046:207] (
)
2010-08-15 15:42:09.902 Tabular[33046:207] (
(
(
Google,
"https://www.google.com/"
),
(
"https://tabular.heroku.com/tab_stores/tester",
"https://tabular.heroku.com/tab_stores/tester"
),
(
"test - Google Search",
"http://www.google.com/search?client=safari&rls=en&q=test&ie=UTF-8&oe=UTF-8"
),
(
"Using POST method in XMLHTTPRequest(Ajax)",
"http://www.openjs.com/articles/ajax_xmlhttp_using_post.php"
),
(
"json stringify - Google Search",
"http://www.google.com/search?client=safari&rls=en&q=json stringify&ie=UTF-8&oe=UTF-8"
),
(
"json.stringify escape - Google Search",
"http://www.google.com/search?q=json.stringify escape&hl=en&safe=off&client=safari&rls=en&ei=PUlaTNiYL4O78gbt8bXsAg&start=10&sa=N"
)
),
(
(
Google,
"https://www.google.com/"
)
)
)
It looks like each item in cellData is itself another array. It's not an NSString, so assigning it to a text field's "text" is unlikely to work (I'm surprised it doesn't crash). When in doubt, call [obj description], which is supposed to return an NSString.
connection:didReceiveData: can be called multiple times per connection. Your commented-out code is on the right track (buffer all the data once and process everything in connectionDidFinishLoading:; alternatively, process it with a stream-based API if you have one; I don't think the JSON library you use supports that).
You curiously make a mutable copy of the data but then don't otherwise mutate it.