Optimize tableView scrolling - iphone

My tableView scrolls with lags if extra populated. Up to 20 cells go well, but above - it starts lagging while scrolling. Please, suggest an implementation with a better scrolling result. Here is the way I did it:
I have defined a custom UITableViewCell class.
The cell has 4 labels and an imageView (each outlet is a synthesized property):
I have placed a tableView in my viewController, and populated it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
MyCustomCell *cell = (MyCustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
for (id currentObject in topLevelObjects)
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (MyCustomCell *) currentObject;
break;
}
}
[cell.label_descr setText:#"bla-bla-bla"];
[cell.label_date setText:#"bla-bla-bla"];
[cell.label_time setText:#"bla-bla-bla"];
[cell.label_numeric setText:#"bla-bla-bla"];
[cell.image_view setImage:[UIImage imageWithContentsOfFile:#"bla-bla-bla"]];
return cell;
}
The amount of text in each cell, as you can see, is miserable, and the image used for the UIImageView is about 25x25 .png file.
My tableView is supposed to hold more than 300 cells (don't blame on me, I have a "customer from hell").
Please, suggest a way to make the tableView scroll smoother, without (much) lags. Or an alternative way to present those "damn-over-300-cells" to my "from hell" customer.
300 thanks in advance!
P.S.: sorry if duplicated, but the solutions found didn't help at all.
EDIT:
About the image used for the imageView:
I use 2 different images only:
a "checkmark" - transaction done
and a "pending" - transaction in process
Maybe I use to define 2 imageView outlets in my tableViewCell xib, and just selecting the needed imageView, instead of setting each time the required image?
SOLUTION FOUND, thanks to everybody, especially to iNoob and max_.
In tableViewCell's xib, I have set the "checkMark" as the default image of the imageView.
When defining the cell's values, in cellForRowAtIndexPath, only if needed, I say:
if_I_should_present_a_pending_image:
[cell setPending];
to replace the "checkMark" with a "pending" image (method defined in tableViewCell class):
- (void)setPending{
self.image_view.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"pending_1.png"],
[UIImage imageNamed:#"pending_2.png"],
[UIImage imageNamed:#"pending_3.png"],
[UIImage imageNamed:#"pending_4.png"],
nil];
self.image_view.animationDuration = 2.0;
self.image_view.animationRepeatCount = 0;
[self.image_view startAnimating];
}
l
After that, the table scrolls like a charm. Thanks to everybody again. Cheers.

Don't iterate through all of the subviews: cell = [topLevelObjects objectAtIndex:0];
Load the images in the background using gcd, and store them in an NSDictionary for easy access:
Pseudo code:
If caches dict contains an object for the URL you want to load
Retrieve that image from the dict
Set it as the image
Else
Load the image using dispatch_async()
Add it to the dict

I found this article suggesting that creating the cells programatically instead of using a nib file could be up to 5-10% faster. I don't know if it's true or not, so take it with a grain of salt, but it may be worth a try.

Replace your code with following one and try it out.
For the below code :
1) Take IBOutlet of your UITableViewCell in the your controller. for below code it is myCustomTableViewCell.
MyCustomCell *customCell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:#"MyCustomCell"];
if(customCell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
customCell = myCustomTableViewCell;
}
Hope it will work.

Related

Custom Table Cell Loading Data Objective C iOS -

Currently I have the following method, but it doesn't quite work...
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"TVCell" owner:self options:nil];
cell = tvCell;
self.tvCell = nil;
}
UILabel *label;
label = (UILabel *)[cell viewWithTag:5];
label.text = [NSString stringWithFormat:#"Hole #%d", indexPath.row];
return cell;
}
The table view gets created with no errors, but each individual cell contains nothing, but clearly the TVCell.xib has a label with a tag of 5. The only question I have is this. I don't quite understand these steps apple gives here...
Select File’s Owner in the nib document window, open the Identity pane of the inspector, and set the class of File’s Owner to your custom view controller class.
Connect the cell outlet of File’s Owner (now the placeholder instance of your custom subclass) to the table-view cell object in the nib-file document.
Here is where those steps are...
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7
Can someone please explain those steps for a noob like me? I think that is what I messed up on, but I could have done anything wrong.
I don't think TcCell becomes a property of self. Try this instead when the cell queue is empty:
if (cell == nil) {
cell=[[NSBundle mainBundle] loadNibNamed:#"TVCell" owner:self options:nil];
}
It's kind of difficult to explain the steps that you said you don't understand in words. I would suggest looking for tutorials that have images or videos for that. (I did a quick search and there are lots available, but I didn't really want to choose one to direct you to without having read them more closely).
However, I prefer to create custom table view cells this way:
http://www.mobilesce.com/2011/12/27/the-best-way-to-do-custom-reusable-uitableviewcells/
It's slightly different from the way described in the apple docs, but I've seen a lot of people use it and I find it easier.
The NSBundle method loadNibNamed:owner:options: returns an NSArray containing the top level objects in your nib file. Try this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"TVCell" owner:self options:nil];
cell = [nibArray objectAtIndex:0];
}

iphone : uitableview : contents of a cell change on scrolling

I have an array which I am using to furnish the contents of custom cell in table view
I dont know whats wrong but when I scroll a tableview the contants oc cell changes dynamically
Please help me to fix this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
MyIdentifier = #"tblCellView";
NSString *offendersImagePath = [self applicationDocumentsDirectory];
//NSLog(#"%#", dbPath);
offendersImagePath=[offendersImagePath stringByAppendingPathComponent:#"Images"];
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = aCustomCell;
aCustomCell=nil;
}
NSMutableArray *tempArray;//=[[NSMutableDictionary alloc] init];
tempArray=[offendersNamesList objectAtIndex:indexPath.row];
//NSLog(#"%d",indexPath.row);
offendersImagePath=[offendersImagePath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg",[tempArray objectAtIndex:0]]];
NSLog(offendersImagePath);
[[cell offendersImageView] setImage:[UIImage imageWithContentsOfFile:offendersImagePath]];
[cell.offendersNameLbl setText:[tempArray objectAtIndex:1]];
[cell.offendersViolation setText:[tempArray objectAtIndex:2]];
//[tempDictionary release];
//[cell setLabelText:[arryData objectAtIndex:indexPath.row]];
return cell;
}
What is typically the problem in these kinds of problems is that you are not setting up the content in your cell correctly in the cellForRowAtIndexPath. When a cell scrolls off the screen the system dumps it into a "Recycle Queue" (my term). As new cells scroll onto the screen the system looks in this recycle queue for cells it can reuse.
((CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];)
If it does not find one it goes ahead and builds an entirely new one from scratch. In your case, it looks like for whatever reason you are not setting up the cell content correctly and the changes you are seeing are recycled cells that have not been updated with the correct content.
I'm not sure exactly where you are going wrong but the code you are using for new cells is a little strange. It should look more like this:
if(cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
I'm not 100% what your issue is, but if it's items that have variable text length and the items on the cell aren't getting re-sized, it's because the sizings of the labels contained in the cell are not getting updated, just their text is getting changed.
A note about doing lookups in the cellForRowAtIndexPath, perhaps you should construct your array(of objects/dictionaries) in viewDidLoad, so as to be more efficient.
Another thing to note is your using .jpg files. It's more optimal to use .png files as they are crushed in size at compile time.
Also using NSMutableArray *tempArray; and hard-coded indexes is very bad practice, if something changes position in the array it means you have to change all your code. Try using a NSDictionary so as the keys are less likely to change.

Custom UITableViewCell problem loading from NIB

UPDATE: I've solved this now - see my own comment. Amazing how often writing out the problem can lead to the resolution when you go through it again!
I must have read every thread on SO and the examples on Apple, but I just can't get this to work in my case and I wonder what I'm doing wrong?
I have made a custom cell in IB with XCode 4. The file's owner is set to the class of the custom view controller for the table and I have an IBOutlet for the UITableView cell which is hooked up OK from what I can tell. I normally do all my table cells via code and use the fast scrolling method, but for this project I really need to use nibs to load the cell content and I'm having some grief I can't figure out.
So in my interface file I have;
#interface myCustomTableViewController <various delegates> {
UITableViewCell *customNibCell;
...
}
#property (nonatomic,assign) IBOutlet UITableViewCell *customNibCell;
In the customTableViewController I do this in my cellForRowAtIndexPath;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#:"customDirCell"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"DirectoryCellWithMenu" owner:self options: nil];
cell = customNibCell;
self.customNibCell = nil;
}
[self configureCell:cell atRow:indexPath.row];
And my configureCell method does this to reach into the cell and pull out the things I wish to customise.
UIImageView *fileImageView = (UIImageView *)[cell viewWithTag:3];
UILabel *headingTextLabel = (UILabel *)[cell viewWithTag:4];
UILabel *modifiedTextLabel = (UILabel *)[cell viewWithTag:5];
UILabel *sizeTextLabel = (UILabel *)[cell viewWithTag:6];
The Problem
The cells do load into the table OK in as much as I can see the correct layout. Furthermore, the custom actions I have defined on various buttons do all fire OK so it seems that the cell is hooked up properly.
BUT none of the customisation works - I can't seem to change the text of any of the labels for example and on investigation, the result of every [cell viewWithTag:nn] is nil which clearly signposts the problem, but I can't see what the cause is.
The identifier for the
UITableViewCell is set to
"customDirCell"
The object identities for the labels
etc are indeed 3,4,5,6 according to
IB
The xib contains exactly 1 object,
the UITableViewCell
I can't see anything wrong with either the nib file creation or the code, but I'm obviously missing something!
Can any kind soul shed light on where I should be looking?
Did you try this
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#:"customDirCell"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DirectoryCellWithMenu" owner:self options: nil];
cell =[nib objectAtIndex:0];
self.customNibCell = nil;
}
I've resolved this as per my comment.

Custom UITableViewCell disappearing after scrolled past top

I have a custom UITableViewCell loaded from a nib file. Everything works fine until I scroll down past the last cell such that the table view has to bounce back when I let go.
Then, the cells towards the top of the list are blank and remain blank until I manually refresh the table view.
Image of issue: [iPhone Screenshot][1]
Here is my cell for row code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TimeEntryCellIdentifier";
TimeEntryCell *cell = (TimeEntryCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TimeEntryCell" owner:self options:nil];
for (id oneObject in nib) {
if ([oneObject isKindOfClass:[TimeEntryCell class]]) {
cell = (TimeEntryCell *) oneObject;
}
}
}
TimeEntry *aTimeEntry = [appDelegate.timeEntries objectAtIndex:indexPath.row];
cell.clientName.text = aTimeEntry.ClientName;
cell.category.text = aTimeEntry.CategoryName;
cell.hours.text = [NSString stringWithFormat:#"%#", aTimeEntry.Hours];
[TimeEntry release];
return cell;
}
Any ideas?
[1]: http://dl.nvthost.com.s3.amazonaws.com/Screen shot 2011-02-18 at 2.46.42 PM.png
[1]: http://dl.nvthost.com.s3.amazonaws.com/Screen shot 2011-02-18 at 2.46.42 PM.png
I don't think you should be doing the
[TimeEntry release];
there on the next to last line. You didn't allocate it in that method, just pulled a reference from the delegate. The retain count is probably zero at that point. When iOS starts releasing memory that TimeEntry will be dealloc'd.
The problem turned out the be the view hierarchy in IB. Even though I put a view in the cell and then dragged UILabels onto the view, they ended up on the same level as the view. After I moved the UILabels under the view it appears to be working.

iphone how to access a custom cell outside of cellForRowAtIndexPath

i have set up a tableview with custom cells. customCell is a class.
heres the code for a more accurate view:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *object = (NSManagedObject *)[entityArray objectAtIndex:indexPath.row];
NSString *cellIdentifier = [NSString stringWithFormat:#"asd%d", indexPath.row];
customCell *cell = [[tableView dequeueReusableCellWithIdentifier:cellIdentifier] autorelease];
//i tried setting a tag but dunno how to call it afterwards
[cell setTag:indexPath.row];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"customCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
if (cell.imagen != nil) {
[[cell imageView] setImage:[cell imagen]];
} else { /* setup image... */ }
}
-(void) webImageReady:(WebImage *)downloadedImage imageView:(UIImageView *)imageView cellTag:(NSInteger *)cTag
{
// This is the part where i want to access cell.imagen, this is actually wrong...
[[[imageView.superview viewWithTag:cTag] imagen] setImagen:downloadedImage.Image];
[imageView setImage:downloadedImage.Image];
}
Ok. now i want to access (reference) the cell.imagen property from a method outside cellForRowAtIndexPath, more precisely at a selector for a download finished (delegated)
Thanks in advance!
Do it inside cellForRowAtIndexPath if the image is downloaded, and on successful download of the image do [tableview setNeedsDisplay]
You shouldn't refer to the cell outside the cell creation method, you should consider the case the cell was rendered but while getting the image was scrolled out the dealloced or even reused for another cell, one way to solve it is to have image view array or something similar.
I think you should try using a third party lib that already doing it(among other things) called Three20. It have an object call TTImageView that gets a URL and loads it in the background, it solves all of the cases along with optimized caching