This is slow because you do everything in tableView:cellForRowAtIndexPath: . TableView:cellForRowAtIndexPath: Is called really ofter, especially each time a cell need to be displayed in your tableview, which includes when your are scrolling your tableView. Thus this method needs to be fast and non-blocking (especially don't do synchronous downloads!
).
Up vote 0 down vote favorite 1 share g+ share fb share tw.
I have an iOS app I'm working on that grabs a bunch of photo URLs from a MySQL database with a JSON request. Once I have these photos and related information, I use it to populate the datasource for a UITableView. I want to create a grid of UIButtons, made out of photos, 4 per row.
This current code works, however it is wildly slow and my phone / simulator freezes right up as I scroll through the table. Tables with only a couple rows work fine, but once I reach 10 or more rows it slows right down and near crashes. I'm new to iOS and objective-c, so I'm assuming it's an inefficiency in my code.
Any suggestions? Thanks! - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSUInteger row = indexPath row; static NSString *CompViewCellIdentifier = @"CompViewCellIdentifier"; UITableViewCell *cell = tableView dequeueReusableCellWithIdentifier: CompViewCellIdentifier; if (cell == nil) { cell = UITableViewCell alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CompViewCellIdentifier autorelease; } // The photo number in the photos array that we'll need to start off with.
NSUInteger photoNumber = (row * 4); // Assemble the array of all 4 photos we'll need for this table row (for this cell). NSMutableArray *rowPhotos = NSMutableArray alloc initWithObjects:self. Photos objectAtIndex:photoNumber, nil retain; NSInteger counter = 1; while (self.
Photos count > photoNumber+counter && counter NSDictionary *photoRow = NSDictionary alloc initWithDictionary:rowPhotos objectAtIndex:i; // Get the photo. NSString *photoPath = NSString alloc initWithFormat:@"http://localhost/photorious%@", photoRow objectForKey:@"path"; NSURL *url = NSURL URLWithString: photoPath; photoPath release; UIImage *cellPhoto = UIImage alloc initWithData:NSData dataWithContentsOfURL:url; // Figure out the container size and placement. Int xCoordinate = ((i*70)+8*(i+1)); CGRect containerRect = CGRectMake(xCoordinate, 0, 70, 70); // Create the Button UIButton *cellPhotoButton = UIButton buttonWithType:UIButtonTypeCustom; cellPhotoButton setFrame:containerRect; cellPhotoButton setBackgroundImage:cellPhoto forState:UIControlStateNormal; cellPhotoButton setTag:(NSInteger)photoRow objectForKey:@"id"; // Add the button to the cell cell.
ContentView addSubview:cellPhotoButton; // Add the action for the button. CellPhotoButton addTarget:self action:@selector(viewPhoto:) forControlEvents:UIControlEventTouchUpInside; cellPhoto release; } rowPhotos release; return cell; } ios uitableview delegates datasource link|improve this question asked Jun 19 '11 at 20:30Eric667 88% accept rate.
This is slow because you do everything in tableView:cellForRowAtIndexPath:. TableView:cellForRowAtIndexPath: Is called really ofter, especially each time a cell need to be displayed in your tableview, which includes when your are scrolling your tableView. Thus this method needs to be fast, and non-blocking (especially don't do synchronous downloads!
) Moreover your don't use the reusability of your tableview cells correctly. This drastically decrease performance as you recreate the content (subviews) for each cell each time. When your cell is reused from a previous one (see it as being "recycled"), you must NOT redo everything, especially you must not re-add every subviews as there already are in the cell itself, as it has been reused and is not a clean new one!
Instead, when dequeueReusableCellWithIdentifier: returns a cell (= an old cell previously created but not used anymore so you can "recycle"/reuse it), you should only change what differs from cell to cell. In your example, typically you will only change the 4 images displayed, but don't recreate the UIImageView, neither add them to as a subview (as these subviews already exists) nor reaffect the target/action. You only need to create the UIImageView, add them a target/action, set their frame and add them as a subview when your are creating a brand new cell, with alloc/initWithReuseIdentifier:/autorelease.
Moreover, you are fetching your images from the network directly in your tableView:cellForRowAtIndexPath:, and synchronously in addition (which means it blocks your application until it finished downloading the image from the net! ). Do an asynchronous download instead, way before your tableView:cellForRowAtIndexPath: (when your app is loaded for example) and store them locally (in an NSArray, or sthg similar for example), and only fetch the local, already downloaded image in your tableView:cellForRowAtIndexPath:.
The thing you are trying to do is not the greatest idea to begin with if you are new to iOS programming. What you wanna do may seem easy, but it implies concepts like asynchronous downloads, MVC design of your app and prefetching the images from the net in your model before displaying them in your view, provide a way to update the tableview when the download is done, and the basic concepts of cell reuse in tableviews. DO read the TableView Programming Guide before going further.
It explains it in details and it really worth reading. Also consult Apple's LazyTableImages sample code which explains how to load images in a tableview lazyly (meaning loading images asynchronously when they are needed), and the URL Loading Programming Guide which explains how to do asynchronous downloads of data. These guides and samples are really worth reading if you want to do what you explain.
There are also a lot of classes to do Grid Views on the net, one of them being my work (OHGridView), but you need to understand basics explained above and in the mentioned guides first before going further.
Fantastic answer, thank you very much. I'll be sure to give the programming guide a good read and check out your class. – Eric Jun 20 '11 at 0:16.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.