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.
Related
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];
});
});
}
I am showing the images in the tableview using NSData dataWithContentsOfURL method but when i scroll the tableview GUI gets hanged.so after searching over the forum i found that i can try with NSUrlConnection method. so i tried but i cant implement it successfully.
Please find my code below...
kindly help me out that how i can correctly done it..
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"DataIdentifier"] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [UIColor colorWithRed:230.0/255.0 green:249.0/255.0 blue:230.0/255.0 alpha:2.0];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
profileName = [appDelegate.arrCommunityUserList objectAtIndex:indexPath.row];
NSString *imgName = [profileName.user_image stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *strValue = [NSString stringWithFormat:#"%d", profileName.userID];
if (tableView == myTableView)
{
cellRectangle = CGRectMake(15, 2, 75, 75 );
NSString *myurl = [NSString stringWithFormat: #"%#pics/photos/%#/%#",ConstantImgURL, strValue,imgName];
NSURL *url = [NSURL URLWithString: myurl];
imageView = [[UIImageView alloc] initWithFrame: cellRectangle];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:myurl]];
[NSURLConnection connectionWithRequest:request delegate:self];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection =[[NSURLConnection alloc] initWithRequest:
request delegate:self];
if (theConnection)
{
receivedData = [[NSMutableData data] retain];
}
[cell.contentView addSubview:imageView];
}
}
return cell;
}
// did receive response
- ( void )connection:( NSURLConnection * )connection didReceiveResponse:( NSURLResponse * )response
//--------------------------------------------------------------------------------------------------
{
NSLog(#"Received response: %#", response);
}
// get recieved data
- ( void )connection:( NSURLConnection * )connection didReceiveData:( NSData * )data
//----------------------------------------------------------------------------------
{
// NSLog(#"Connection received data, retain count: %d", [connection retainCount]);
[receivedData appendData:data];
}
// finished loading
- ( void )connectionDidFinishLoading:( NSURLConnection * )connection
//-------------------------------------------------------------------
{
// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:receivedData];
imageView.image = image;
}
// connection failed with error
- ( void )connection:( NSURLConnection * )connection didFailWithError:( NSError * )connError
//---------------------------------------------------------------------------------------
{
// NSLog(#"Error receiving response: %#", connError);
[connection release];
[receivedData release];
}
dataWithContentsOfURL is a synchronous network request. That means that where your code is called it will wait until the request finishes before moving on to the next instruction. Synchronous networking is bad. Really bad. It only really ever works in testing.
What you should be doing, is firing off asynchronous requests for these images. The reason why your code above is horrendously slow is that every single time that the tableView asks its dataSource delegate for cellForRowAtIndexPath:; your code fires off a network request synchronously - which means the cell won't be returned until the network request for the image is finished.
Instead, what you should be doing is either loading all the images asynchronously when the tableView is requested. Here's a good example which uses tags to identify them as they return. This is not easy in the whole context of what you're doing; so perhaps you might want to start all the NSURLConnections when the tableView is shown, return 0 for numberOfSectionsInTableView until the connections finish, then call reloadData on the tableView when they're all finished (and make numberOfSectionsInTableView now return the right number of rows to display).
Start using ASI library: http://allseeing-i.com/ASIHTTPRequest/How-to-use
The sooner the better.
I think this might solve your Problem... http://www.markj.net/iphone-asynchronous-table-image/
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
I am trying to parse using JSON Parser and the result which i get i have to put it into table view. I've passed a constant key value and a string .
Is there parsing steps wrong? or missed.
I have included the code for the JSON parser.
Thanks in advance.
SBJSON *parser = [[SBJSON alloc] init];
NSString *urlString =[NSString stringWithFormat:#"http://api.shopwiki.com/api/search?key=%#&q=%#",apiKey, string];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSMutableArray *statuses = [[NSMutableArray alloc]init];
statuses = [parser objectWithString:json_string error:nil];
NSLog(#"Array Contents: %#", statuses);
NSMutableArray *statuses0 = [[NSMutableArray alloc]init];
statuses0 = [statuses valueForKey:#"offers"];
NSLog(#"Array Contents: %#", statuses0);
//For Title
NSMutableArray *statuses1 = [[NSMutableArray alloc]init];
statuses1 = [[[statuses valueForKey:#"offers"] valueForKey:#"offer"]valueForKey:#"title"];
NSLog(#"Array Contents 4 Title: %#", statuses1);
Here in statuses1 array i get 20 objects which are all titles, now i just want to display that titles into tableview:-
snippet code for tableview:-
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"status count:%d",[statuses1 count]);
return [statuses1 count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Inside Tableview");
int counter=indexPath.row;
NSString *CellIdentifier = [NSString stringWithFormat:#"%d",counter];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle=UITableViewCellSelectionStyleNone;
}
NSLog(#"Inside Tableview 1");
cell.textLabel.text=[statuses1 objectAtIndex:indexPath.row];
NSLog(#"Inside Tableview 2");
return cell;
}
I get Bad excess exception whten it hits on :-
cell.textLabel.text=[statuses1 objectAtIndex:indexPath.row];
Please give me the solution
thanks in advance:-
If you're getting EXC_BAD_ACCESS on that line, it's probably because statuses1 has been released by the time cellForRowAtIndexPath is called. What block of code is this line in?
NSMutableArray *statuses1 = [[NSMutableArray alloc]init];
The statuses1 variable above is local to whatever scope you've declared it in. Do you then assign it to your UITableViewController.statuses1 ivar? Is that a retained property?
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]);
}