Navigating in UITableView Slow with JSON Issue - iphone

I am loading 4 cities string in the table view which is working but when I select one of the cell and navigate to the other table it navigate too slow. I am using the same code below in the the other table with different link. Could you please tell me why it takes long time (~4 - 6 Sec) to get to the other view?
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL * url = [NSURL URLWithString:#"http://kalkatawi.com/jsonTest.php"];
NSData * data = [NSData dataWithContentsOfURL:url];
NSError *e = nil;
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&e];
jsonArray1 = [[NSMutableArray alloc] init];
for(int i=0;i<[jsonArray count];i++)
{
NSString * city = [[jsonArray objectAtIndex:i] objectForKey:#"city"];
[jsonArray1 addObject:city];
}
-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
NSString *tempString = [jsonArray1 objectAtIndex:indexPath.row];
cell.textLabel.text = tempString;
return cell;
}
-
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SeconfViewController *second = [[SeconfViewController alloc] initWithNibName:#"SeconfViewController" bundle:nil];
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *cellText = selectedCell.textLabel.text;
NSString *edit = [NSString stringWithFormat:#"http://kalkatawi.com/jsonTest.php?d=1&il=%#", cellText];
second.str2 = edit;
[self.navigationController pushViewController:second animated:YES];
}

It takes more time to navigate on the other screen because you are synchronously loading the data from the server. In iOS all the UI is done on the main thread, and by making your data call on the main thread you are blocking it. The best way I know to handle this is to use GCD (Grand Central Dispatch). It's an API in iOS that will spawn threads for you without a hassle. You just need to tell that you want to make the call to load data from the server on a background thread. When you do that, view should navigate instantaneously. You can use an Activity indicator while the data comes.
dispatch_async(dataQueue, ^{
// Load all your data here
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
});
});

Maybe because you are downloading synchronous and you are blocking the main thread maybe that is the reason that your app freeze for 4-6 sec, try to download your json asynchronous
- (void)viewDidLoad
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *response = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://kalkatawi.com/jsonTest.php"]];
NSError *parseError = nil;
jsonArray = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingAllowFragments error:&parseError];
jsonArray1 = [[NSMutableArray alloc] init]
for(int i=0;i<[jsonArray count];i++)
{
NSString * city = [[jsonArray objectAtIndex:i] objectForKey:#"city"];
[jsonArray1 addObject:city];
}
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self.myTableView reloadData];
});
});
}

Related

Return to collectionView function after connectionDidFinishLoading

My question probably has a really obvious answer which I'm missing. I have an NSURLConnection like so:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *urlstring = [NSString stringWithFormat:#"http://127.0.0.1:8000/image/"];
NSMutableURLRequest *postRequest = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:urlstring]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[postRequest setHTTPMethod:#"POST"];
self.firstConnection = [[NSURLConnection alloc] initWithRequest:postRequest delegate:self];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
if (connection == _firstConnection){
// Deal with the data
[self getImages];
[self.collectionView reloadData];
}
-(void)getImages
{
NSError *error;
NSMutableArray* images= [NSJSONSerialization JSONObjectWithData:_data options:0 error:&error];
NSUInteger arrayLength = [images count];
dressURLS = [[NSMutableArray alloc] init];
for (NSInteger i=0;i<arrayLength;i++)
{
NSString *temp = [images[i] objectForKey:#"image"];
[dressURLS addObject:temp];
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *dressImageView = (UIImageView *)[cell viewWithTag:100];
NSString *clothesurl = dressURLS[i]; //i value????
NSString *url =[NSString stringWithFormat:#"http://127.0.0.1:8000/media/%#",clothesurl];
NSURL *imageURL = [NSURL URLWithString:url];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
dressImageView.image = [UIImage imageWithData:imageData];
});
});
return cell;
}
The code is quite messy, I'm aware. This is how it works. viewDidLoad initializes the connection. It goes to connectionDidFinishLoading where getImages deals with the data which I had to manipulate and store in an array. Then I used the reloadData method to go to the Collection View which is where I run into all kinds of problems.
I need to access the elements of dressURLS[i] where i=0,1,2,3. But the looping is severely complicated by the fact that a. The collection view gets reloaded b. The asynchronous dispatch. I am unable to get i to loop from 0 to 3.
Any solutions to make this less complicated?
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
if (connection == _firstConnection){
// Deal with the data
[self getImages];
[collectionView reloaddata]; // or self.collectionView (for property)
}
}
"Discussion
Call this method to reload all of the items in the collection view. This causes the collection view to discard any currently visible items and redisplay them." - Apple
You can the BOOL for the first time & for second time check the condition & display the images by reloading the Collection View.

Reload TableView Data from DB

In my app i git dat from DB by php file in internet and data shown in a Table view but i need to reload my data every minute, i can get the new data every minute but i cant replace it in the table view.
1) Do you know how to do it?
2) If you know how to get my code more simple, and delete that unNeeded codes? I'll be thanks to you.
My ViewController.m
#import "ViewController.h"
#import "CJSONDeserializer.h"
#implementation ViewController
#synthesize tblMain, rows;
NSURL *url;
NSString *jsonreturn;
NSData *jsonData;
NSError *error;
NSDictionary *dict;
UITableViewCell *cell;
static NSString *CellIdentifier;
- (void)viewDidLoad{
[super viewDidLoad];
[self GetData];
}
-(void)GetData{
url = [NSURL URLWithString:#"http://ar2.co/savola/"];
jsonreturn = [[NSString alloc] initWithContentsOfURL:url];
NSLog(jsonreturn);
jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
dict = [[[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error] retain];
rows = [dict objectForKey:#"savola"];
NSLog(#"Array: %#",rows);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [rows count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
dict = [rows objectAtIndex: indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# - %#", [dict objectForKey:#"company"], [dict objectForKey:#"price"]];
cell.detailTextLabel.text = [dict objectForKey:#"time"];
return cell;
}
#end
To force a table to reload its information, call [self.tableView reloadData] from your view controller class. That will call all of the data source methods again, and it will update the display as well.
You should reload the table after fetching the new data from server. Like in your GetData method :
- (void)GetData
{
url = [NSURL URLWithString:#"http://ar2.co/savola/"];
jsonreturn = [[NSString alloc] initWithContentsOfURL:url];
NSLog(jsonreturn);
jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
dict = [[[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error] retain];
rows = [dict objectForKey:#"savola"];
NSLog(#"Array: %#",rows);
[yourTable reloadData];
}
You need to use reloadData method to reload your table view to display the new data.
But as you mentioned- "but i need to reload my data every minute"
So for this you can check the recieved data with existing data, and if something is changed in new data then only reload the table, else skip that.
If you reload your table every one minute unneccesary without change in data, that will be not correct and also that will slow down your app.
In this kind of situation I would recommend you look at using NSFetchedResultsController. It will automatically update you table only when it is required.

self performSelector:#selector(loadData) withObject:nil - not working

I use:
self performSelector:#selector(loadData) withObject:nil...
it look like working with some command only in "loadData" but the rest are not.
Here is my viewdidload:
- (void)viewDidLoad
{
[super viewDidLoad];
[mActivity startAnimating];
[self performSelector:#selector(loadData) withObject:nil afterDelay:2];
//[mActivity stopAnimating];
}
and here is loadData:
-(void)loadData
{
[mActivity startAnimating];
NSLog(#"Start LoadData");
AppDelegate *delegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *selectData=[NSString stringWithFormat:#"select * from k_proverb ORDER BY RANDOM()"];
qlite3_stmt *statement;
if(sqlite3_prepare_v2(delegate.db,[selectData UTF8String], -1,&statement,nil)==SQLITE_OK){
NSMutableArray *Alldes_str = [[NSMutableArray alloc] init];
NSMutableArray *Alldes_strAnswer = [[NSMutableArray alloc] init];
while(sqlite3_step(statement)==SQLITE_ROW)
{
NSString *des_strChk= [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement,3)];
if ([des_strChk isEqualToString:#"1"]){
NSString *des_str= [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement,4)];
[Alldes_str addObject:des_str];
}
}
Alldes_array = Alldes_str;
Alldes_arrayAnswer = Alldes_strAnswer;
}else{
NSLog(#"ERROR '%s'",sqlite3_errmsg(delegate.db));
}
listOfItems = [[NSMutableArray alloc] init];
NSDictionary *desc = [NSDictionary dictionaryWithObject:
Alldes_array forKey:#"description"];
[listOfItems addObject:desc];
//[mActivity stopAnimating];
NSLog(#"Finish loaData");}
it give me only printing 2 line, but did not load my Data to the table, but if I copy all the code from inside "loadData" and past in "viewDidLoad", it load the data to the table.
Any advice or help please.
A few things: If you see any NSLog output at all, then the performSelector is succeeding. You should change the title of your question.
If you are trying to load data into a table, the method should end with telling the UITableView to reloadData (or a more elaborate load using begin/end updates).
If the listOfItems is the data supporting the table, get this working first by hard-coding something like this:
-(void)loadData {
listOfItems = [NSArray arrayWithObjects:#"test1", #"test2", nil];
[self.tableView reloadData];
return;
// keep all of the code you wrote here. it won't run until you remove the return
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *string = [listOfItems objectAtIndex:indexPath.row];
cell.textLabel.text = string;
return cell;
// keep all of the code you probably wrote for this method here.
// as above, get this simple thing running first, then move forward
}
Good luck!

Help me on loading the images on table view using NSOperationQueue

Kindly look the below code..i found from web
- (void) loadData {
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadDataWithOperation) object:nil];
[queue addOperation:operation];
[operation release];
}
- (void) loadDataWithOperation {
NSURL *dataURL = [NSURL URLWithString:#"http://icodeblog.com/samples/nsoperation/data.plist"];
NSArray *tmp_array = [NSArray arrayWithContentsOfURL:dataURL];
for(NSString *str in tmp_array) {
[self.array addObject:str];
}
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell.textLabel setText:[self.array objectAtIndex:indexPath.row]];
return cell;
}
I have found the above code which loads the text from the web without any problem.( i mean gui is not hanging ).like wise i want to load images into the table view.for that i have wriiten the below code.
- (void) loadDataWithOperation {
NSString *Img_id, *Img_name, *DynamicImgUrl;
Img_id = xmltag.equip_id;
Img_name = xmltag.image;
DynamicImgUrl = [NSString stringWithFormat:#"http://test.com/pics/equipment/%#/%#",Img_id, Img_name];
NSURL *ImageUrl = [NSURL URLWithString:DynamicImgUrl];
//UIImage *image = [UIImage imageWithData: [NSData dataWithContentsOfURL:ImageUrl]];
NSArray *tmp_array = [NSArray arrayWithContentsOfURL:ImageUrl];
for(NSString *str in tmp_array) {
[self.array addObject:str];
}
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
Am i correct here? how can i go with adding into the table view...
Kindly help me out...
U are seeking to display images on Tableview from URL, when u download image from url we need to consider some issues, it will freeze ur table view if u goahead with ordinary loading image.
i suggest u look at LazyLoading image
u see this link http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html
Download this sample and get to know the things

lazy loading images in UITableView iPhone SDK

I need to implement the lazy loading concept for the images in my tableview, so that the user will be provided with the textual data initially and later the images.
How can i implement this in to my app.. help needed.. please
Thanks in advance
Shibin
What i have created for a project of mine works as follows;
Extend the UITableViewCell class by a category in "UITableViewCell+Async.h"
(See some examples if you are not sure what the category thing in Obj C is)
#interface UITableViewCell (Async)
-(void)loadAsyncImage:(NSString*)url withIndex:(NSInteger)index inWidth:(NSInteger)width inHeight:(NSInteger)height;
-(void)loadAsyncBackground:(NSMutableArray*)parameters;
#end
And then in the implementation file "UITableViewCell+Async.m"
#import "UITableViewCell+Async.h"
#implementation UITableViewCell (Async)
-(void)loadAsyncImage:(NSString*)url
withIndex:(NSInteger)index
inWidth:(NSInteger)width
inHeight:(NSInteger)height {
NSMutableArray* parameters = [NSMutableArray arrayWithCapacity:2];
[parameters addObject:url];
[parameters addObject:[NSNumber numberWithInteger:index]];
[parameters addObject:[NSNumber numberWithInteger:width]];
[parameters addObject:[NSNumber numberWithInteger:height]];
self.imageView.tag = index;
[self performSelectorInBackground:#selector(loadAsyncBackground:) withObject:parameters];
}
-(void)loadAsyncBackground:(NSMutableArray*)parameters {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString* url = [parameters objectAtIndex:0];
NSInteger index = [[parameters objectAtIndex:1] integerValue];
NSInteger width = [[parameters objectAtIndex:2] integerValue];
NSInteger height = [[parameters objectAtIndex:3] integerValue];
UIImage* image = [Utils getImageResized:url inSize:CGSizeMake(width, height)];
if (self.tag==index) {
self.imageView.image = image;
[self setNeedsLayout];
}
[pool release];
}
#end
This basically adds a functionality to the UITableViewCell to load an image in a new background thread, resize the image and set it to the imageview. Checking the tag is added to see if the cell is still waiting for the image, since it can be re-used and another thread for the image could be downloading another image for that re-used cell...
The function in the above code with the signature of;
+(UIImage*)getImageResized:(NSString*)url inSize:(CGSize)size;
checks a local cache of images, downloads the image from the web if not in the cache, saves it to local cache, resizes the image in the given size and returns the image, all done in a sync(blocking) method call. Since this is already the background thread, no harm blocking it for this operation. When the method returns the image, it is set to the cell's imageview if it still has the same tag(not re-used for some other row)
In the cellForRowAtIndexPath method you can add the new category and you should be done;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"......."];
if (cell == nil) {
................
}
................
[cell loadAsyncImage:deal.logo withIndex:indexPath.row inWidth:40 inHeight:40];
................
return cell;
}
Do you mean this
http://developer.apple.com/iphone/library/samplecode/LazyTableImages/Introduction/Intro.html
I used ASINetworkQueue for lazy loading.
In .h file
#import "ASINetworkQueue.h"
and define a variable
//array contains url address of images and a variable of ASINetworkQueue;
NSMutableArray *arrImg;
ASINetworkQueue *queue;
In .m file
static int rowNo = 0;
- (void)downloadImages{
if(queue == nil){
queue = [[[ASINetworkQueue alloc] init] autorelease];
}
for(NSString* urlString in arrImg)
{
NSURL *url = [NSURL URLWithString:urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
[queue addOperation:request];
}
[queue go];
}
- (void)requestDone:(ASIHTTPRequest*)req{
for (int i = 0; i < [arrImg count]; i++) {
NSMutableString *d = [[NSMutableString alloc] init];
d = [arrImg objectAtIndex:i];
//comparing the url to get correct row no of cell
if([[req.url absoluteString] isEqualToString:d]){
rowNo = i;
break;
}
}
//creating a cell with rowNo
NSIndexPath *index = [NSIndexPath indexPathForRow:rowNo inSection:0];
UITableViewCell *cell = (UITableViewCell *)[roomTable cellForRowAtIndexPath:index];
UIImageView *v = (UIImageView *)[cell.contentView viewWithTag:10];
v.image = [UIImage imageWithData:[req responseData]];
[cell release];
cell = nil;
[v release];
v = nil;
}
- (void)requestWentWrong:(ASIHTTPRequest*)req{
for (int i = 0; i < [arrImg count]; i++) {
NSMutableString *d = [[NSMutableString alloc] init];
d = [arrImg objectAtIndex:i];
//comparing the url to get correct row no of cell
if([[req.url absoluteString] isEqualToString:d]){
rowNo = i;
break;
}
}
NSLog(#"No data for cell : %d",rowNo);
NSLog(#"Request returned an error %#",[req error]);
}