TableView delays when scrolling - ios

My TableView delays for loading cell when user scrolls. I found similar questions, but people in those questions using images or downloads something, or have 100+ cells. In my case I have four different cell types in one tableview (is it problem?) and one of them have various count and delays before load new one.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
UITableViewCell *tvCell = [tableView dequeueReusableCellWithIdentifier:identifierUpper];
if (!tvCell) tvCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifierUpper];
tvCell.selectionStyle = UITableViewCellSelectionStyleNone;
_collectionMainInfoView.$width = self.view.$width;
_collectionMainInfoView.$height = self.view.$height*0.45;
[tvCell.contentView addSubview:_collectionMainInfoView];
[tvCell.contentView setBackgroundColor:[UIColor clearColor]];
[tvCell setBackgroundColor:[UIColor clearColor]];
return tvCell;
}
if (indexPath.row == 0) {
EventsCell *eventCell = [tableView dequeueReusableCellWithIdentifier:identifierEvent];
[eventCell setEvent:[Event getNextEvent]];
return eventCell;
}
if (indexPath.row == soldiers.count + 1) {
InviteOtherCell *cellInvite = [tableView dequeueReusableCellWithIdentifier:identifierInvite];
return cellInvite;
}
else {
ProfilesCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.curSoldier = _curSoldier;
cell.delegate = self;
// [cell configureCellWithSoldier:soldiers[indexPath.row - 1]];
[cell setRightUtilityButtons:[self rightButtons] WithButtonWidth:120];
return cell;
}
}
The last ones ( after "else {") has delay before showing
#ProfilesCell.m
- (void)awakeFromNib {
[super awakeFromNib];
_cardView.layer.shadowColor = [UIColor blackColor].CGColor;
_cardView.layer.shadowOffset = CGSizeMake(1,1);
_cardView.layer.shadowRadius = 2;
_cardView.layer.shadowOpacity = 0.2;
}

this code looks litlle bit messy to me.
1) you've defined some view _collectionMainInfoView that will be added to every dequeued cell in first section, thus you're not only retaining that view, but also readding it to your table view cell everytime this delegate method is called.
I would propose to just create own subclass of cell that already contains main info view.
After that, you'll be able to register this UITableViewCell's subclass for your table view, thus you will get rid of this code
if (!tvCell) tvCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifierUpper];
don't hold _collectionMainInfoView in your VC. That smells. How this view will be reused when you'll have multiple cells in first section? You'll probably end up with "jumping view" that will be referenced in several cells.
2) We don't see your implementation of ProfilesCell, thus it's pretty hard to say what could go wrong. If possible, try to setup your cell's view hierarchy directly in it's init. If that's not possible. Don't generate buttons [self rightButtons] everytime this delegate method is called.
You're also not following Objective-C code guidelines. Try to get through them.
For example in here:
[cell setRightUtilityButtons:[self rightButtons] WithButtonWidth:120];
your method parameters should start with lowercase
[cell setRightUtilityButtons:[self rightButtons] withButtonWidth:120];
3) For better readability I'd use switch in your control flow instead of if statement

The only reason that your UITableView scroll not smooth is that you execute long async works in main thread when you load a Cell. Try to find out which piece of code that can execute long work in async?
Try to comment your 2 lines (just for test). What do you do in those 2 set function?
cell.curSoldier = _curSoldier;
and
[eventCell setEvent:[Event getNextEvent]];

When scrolling is not smooth, normally async work is performed when the cell is being displayed. Based on your code I would suspect 2 lines:
1.
cell.delegate = self;
Not really sure what you want to do here. It may cause a loop in the reference?
2.
Sometimes the new optimalisation rules in the compiler have problems with commands within commands. Like in your statement:
[cell setRightUtilityButtons:[self rightButtons] WithButtonWidth:120];
You can try to break this up in 2 separate statements and a new variable.

Unrelated to your tableview code, but creating shadows can cause a performance hit, especially if you are creating a lot. I suggest using theshadowPathproperty, it will speed up your code dramatically.

Related

Using TableView with many custom Cells and autolayout

I want to refactor some Code for better Performance, but my Problem is i´m not sure how to do it. At the Moment i have one UIViewController with a UIScrollView on it.
I also have 20 different Views which (each has its on .h and .m File) can be laid fully dynamically on my UIScrollView. Every Time i start the UIViewController i send a request to my Server and then i get the Response and then i know how many Views i have to put on the UIScrollView.
So you can imagine when theres a lot of different views on my UIScrollView it takes a few seconds because alle Views are getting fully loaded, before the User can finally interact with them.
So my idea is to replace the UIScrollView with a UITableView and change all the CustomViews (UIViews) to Custom UITableViewCells. So only the visible Cells would be loaded at the first start!
Know i have several Problems.
At the Moment, most of the Code from my CustomViews are build with Frames, but i want to change it completely to Autolayout, i don´t think it makes sense to build them all with the IB (xib files ...). So i have to do the whole Autolayout Stuff in Code?
Some of the Custom Views are really big, so they getting really high, and some can be really small. My concern is that the scrolling Perfomance will be really bad... because i cannot really use estimatedRowHeight(A Example: sometimes one Cell can get a height of 1000.0f and the next cell only 40.0f).And in Combination with Autolayout and the Time i have to wait until the Response frome my Server arrives, i think it could be really annoying for the User.
There can be up to 20 different!! custom rows, makes it really sense to use a UITableView in this case? As i mentioned before, they are all very different in their Size and Content!
Here is a little Part of my new Code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.node.blocks count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
GTBlockView *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
GFBlock *block = [self.node.blocks objectAtIndex:indexPath.row];
//createInterfaceforBlock -- Here the Cell gets called and the Content and the Size gets defined
cell = [[GTAppController sharedInstance] createInterfaceForBlock:block];
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//GTBlockView is the SuperView of all my Custom Cells
GTBlockView *cell = [self.offscreenCells objectForKey:CellIdentifier];
if (!cell)
{
cell = [[GTBlockView alloc] init];
[self.offscreenCells setObject:cell forKey:CellIdentifier];
}
GFBlock *block = [self.node.blocks objectAtIndex:indexPath.row];
//createInterfaceforBlock -- Here the Cell gets called and the Content and the Size gets defined
cell = [[GTAppController sharedInstance] createInterfaceForBlock:block];
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
[cell setNeedsLayout];
[cell layoutIfNeeded];
// Get the actual height required for the cell
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
height += 1;
return height;
}
Maybe some of you have some better Ideas or some good Sources?
I have done exactly as you are thinking in an app of my own - I first went down the UIScrollview route, then I changed to a UITableview with many kinds of custom cells. It does make total sense and is very worth it - I gained a massive performance increase from the uitableview. One of the major problems with the UIScrollview is that it will work out the autolayout for all the content in your contentView, which as you say, can take several seconds, but the UITableview will handle this much more quickly and efficiently. So - go for it.
I would strongly suggest you use a unique XIB file for each custom cell. Do the autolayout in each one individually, and that way you are much more likely to avoid problems down the line. Programmatic constraints are far harder to maintain, and autolayout is often challenging.
To get it all working, I did the following : first I had a subclass of UITableviewCell that was the parent class for all the other cell objects. In my case this was called SNFormTableCell (UITableviewCell). Then all other cell objects were based on this class. In my cellForRowAtIndexPath method, I do this :
ReportItem *objectForCell = [self.reportSet reportItemForSection:indexPath.section andRow:indexPath.row]; //my personal data class - just contains the cell data I need
NSString *identifier = [self getNibNameAndReusableIdentifierNameForObjectType:objectForCell.cellType.integerValue];
SNFormTableCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell)
{
[self registerNib:[UINib nibWithNibName:identifier bundle:nil] forCellReuseIdentifier:identifier];
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.contentView.clipsToBounds = YES;
}
[cell setSnFormTableCellDelegate:self]; //i have a delegate that calls back to the tableview when cells are interacted with
cell.reportItem = objectForCell; //put the data object onto the cell to do with as the cell requires
[cell refreshUI]; //update the UI using the data - this is over-ridden by the various subclasses of the cell
The method in there called getNibNameAndReusableIdentifierNameForObjectType .. looks like this (just gets the identifier we need for nib and re-use):
- (NSString*) getNibNameAndReusableIdentifierNameForObjectType:(SNFormTableObjectType)objectType {
if (objectType == SNFormTableObjectTypeBoolean) return #"SNBooleanCell";
if (objectType == SNFormTableObjectTypeDatePicker) return #"SNDatePickerCell";
if (objectType == SNFormTableObjectTypeDropDown) return #"SNDropDownCell";
if (objectType == SNFormTableObjectTypeDropDownPlusSingleLineText) return #"SNDropDownPlusSingleLineTextCell";
... etc
}
Then finally, the parent cell class has a method called -(void) refreshUI. So I put the data object onto that cell - this contains all the data I might need for the cell. The the subclasses over-ride this refreshUI method in their own specific way, to use the data as they need to.
Just to re-iterate, I gained enormously from going down this route. A scrollview with a lot of content, taking 5 or more seconds to load the nibs and calculate the autolayout (on the main thread too, making the app unresponsive), would appear instantly on the UITableview version. So go for it. If you need any more details on how to go about it, let me know.

IOS7 Custom TableViewCell will not appear

I am trying to create a project with a custom UITableViewCell. The custom cells never load, they're just blank. At this point in the project what I'm trying to do is placing a UITableViewCell in a .xib, designing it the way I want and specifying its reuse identifier along with tag IDs for the components so that I can use them in code later on.
I've googled a ton and found several tutorials that look like what I want to do, along with many SO questions that have answers that seem applicable. At this point it's probably just my head spinning with too many different angles and solutions.
This is my current attempt at trying to register the custom cell with my UITableView, yet when running this on a device the rows in the table view are entirely blank.
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
}
UILabel* l1 = (UILabel*)[cell viewWithTag:1];
UILabel* l2 = (UILabel*)[cell viewWithTag:2];
UILabel* l3 = (UILabel*)[cell viewWithTag:3];
l1.text = #"Foobar";
l2.text = #"Foobar";
l3.text = #"Foobar";
I'm pretty certain that I've hooked up all the properties and such correctly, but at this stage I need a fresh pair of eyes to point out the facepalm for me.
The interesting files are FilmerView.m/h/xib and the cell is in FilmerViewCell.xib. When running the app this TableView is in the second tab of the tab bar controller.
Project:
http://speedy.sh/WhhpP/test12.zip
I can't provide a full answer atm but look up the tableview method. registerNib:forCellReuseIdentifier:
Also, stop using that dequeue method. Use the one that includes the indexPath.
Then you don't have to check if the cell is nil afterwards.
EDIT
In viewDidLoad (or somewhere like that)...
UINib *cellNib = [UINib nibWithNibName:#"MyCustomCellXibFileName" bundle:[NSBundle mainBundle]];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"CellIdentifier"];
Now in the table view datasource method...
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier" forIndexPath:indexPath];
// no need to check cell == nil.
// this method is guaranteed to return a non nil cell.
// if it doesn't then the program will crash telling you that you need to...
// register a class or nib (but we just did this in viewDidLoad) :D
// configure your cell here...
[self configureMyCell:(MyCustomCell *)cell atIndexPath:indexPath];
return cell;
}
- (void)configureMyCell:(MyCustomCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.nameLabel.text = #"Hello, world";
}
Hope this helps.
Make sure that you have set datasource and delegate properties of your tableView.
Make sure that you have implemented (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method and it returns a positive value (>0).
Evaluate the following:
Is the ReuseIdentifier set in the XIB. See Properties Tab in Interface Builder on the right when selecting the cell.
Are the AutoresizingMasks set properly for the labels to be visible?
WrapModes: Which did you select? When having wrapmode WrapWord, is the font size too large for the text to be moved in the next line becoming invisible?
Set the background color of the UITableViewCellss content view to something else than white or transparent, as well as the background colors of the labels to see if the cell is even there.
Manually call numberOfRowsInSection on your table, pass the proper NSIndexPath identifying the target section and see if its greater 0 to confirm that the TableView even attempts to load the data, thus, the cells. ( Alternatively set a breakpoint in the method or do a NSLog. )
Do a NSLog in cellForRowAtIndexPath to confirm that the cell returned is not nil and the method is even called!

create UITableViewCell programmatically

I am trying to create a UITableViewCell that contains a UIScrollView that is able to scroll horizontally for each cell in the UITableView.
Everything shows correctly and works well. However, when I scroll constantly up and down on the UITableView, memory usage goes up and up and up and up..... which I think means that I am constantly adding the custom elements over each over when the UITableViewCell is being reused. I would like to know how I can stop this from happening.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Configure the cell...
NSDictionary *cellDictionary = [xmlMArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// code
codeString = [[UILabel alloc] initWithFrame:CGRectMake(15.0, 0.5, 70.0, 40.0)];
codeString.text = [cellDictionary objectForKey:#"Code"];
codeString.backgroundColor = [UIColor clearColor];
// series
addressString = [[UILabel alloc] initWithFrame:CGRectMake(220.0, 10.5, addressString.frame.size.width, 50.0)];
addressString.text = [NSString stringWithFormat:#"PC %#: %#",[cellDictionary objectForKey:#"Number"] ,[cellDictionary objectForKey:#"Street"]];
[addressString sizeToFit]; // Dynamic UILabel width
addressString.backgroundColor = [UIColor clearColor];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIScrollView *scrollCell = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, cell.frame.size.width, cell.frame.size.height)];
[scrollCell setContentSize:(CGSizeMake((220.0 + addressString.frame.size.width)+15, cell.frame.size.height))];
[scrollCell addSubview:codeString];
[scrollCell addSubview:addressString];
[cell addSubview:scrollCell];
return cell;
}
There seems to be two problems here. I'll try to explain what's actually happening, but you should follow mbm29414's answer on what to do.
In the first part of this method, you are asking for a UITableViewCell using the identifier:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
The next part is to check if you received a cell with this call:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
This means that if your first line of code DID NOT return a cell-object, then you are instantiating a new one.
Later on in your method, you are instantiating yet another cell:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
UIScrollView *scrollCell (...)
This way you might not actually be reusing the cells, I'm not sure. At the very least, it should not be there. You potentionally allocate double the space each time.
Remove the last instantiation, and that should probably help a little.
The other problem, I think, is that you're adding scrollView and UILabels to your cells' subviews. Your circle of life:
Create cell
Create 2 UILabels
Create ScrollView
Add labels to ScrollView's subview
Add scrollView to Cell's subview
Send cell on it's merry way
When your cell is brought back from the dead during [tableView dequeueReusable..], they still contain the UIScrollView with the UILabel. Your code does not take advantage of that, but rather ignores it. This means that you are adding an ADDITIONAL scrollView with labels into your cell. If you scroll up and down a lot, this means that one single cell can possibly contain 50 different scrollViews, all of them taking the same amount of processing.
This is what happens next:
Get cell from the queue
(this cell already contains UIScollView and UILabels)
Add new scrollView with labels anyway
Send it on it's merry way (now with 2 scrollViews and 4 labels)
To solve this, you should do as mbm29414 suggested, to make your own subclass of UITableViewCell. That way, you can say cell.codeString.text=#"blah";
While you are re-creating a new UITableViewCell each time, you also appear to be endlessly adding UIScrollView and UILabel objects.
First, remove the second call of:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
Second, try subclassing UITableViewCell, making the UI like you like it either in an init method or in IB. Then, make a "setup" method that takes an object and configures each UI element accordingly. That way, you can not only recycle your cells, but also keep from continually creating more UIView subclass instances.
Couple of things.
Your check to see if the table view cell returned by
dequeueReusableCellWithIdentifier: is nil is not necessary, because
that method won't return nil anymore unless you've made a mistake with your identifier. It used to (I think before iOS
6).
Later in the code you're creating a new cell and assigning it to the cell variable which is eventually returned. Why? You already created one, so creating a second is fundamentally wrong.

set selected tag for collectionView

I made a UICollectionView with some cells in it and it displayed correct, now I want to set a selected tag for one or more cells, in custom cell, I can use two ways to implement it:
way 1: set selectedBackgoundView
self.selectedBackgroundView = backgroundView;
way 2: add a UIImageView as selected tag
[_coverImageView addSubview:_selectImageView];
//coverImageView is image for cell,
//selectImageView is a tag imageView for selected.
then the problem comes up:
For example I selected the first cell, When I scroll the UICollectionView, way 1 still displayed the first cell selected, but with way 2, the _selectImageView would be added to the other cell.
I know it is caused by Reuse Cell,but have no idea for deal with it.
Rather than adding your selected tag after you've created the cell, you should add it at the point of creation.
You don't say how you're creating your custom collection view cells, but it sounds as if you might not be using your own subclass, and are simply adding what you need to a plain UICollectionViewCell. You will find it much easier to create your own subclass, and set it up with an image view exposed that can be enabled/disabled as required. You can create custom cells either entirely in code, or in conjunction with a XIB - whichever you prefer.
Recently I am working on a similar project. Although It's long ago, but I hope to help someone who need it.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:collectionCellID forIndexPath:indexPath];
if (cell == nil) {
cell = [[MyCollectionViewCell alloc]init];
}
//Change Selected State
if([[collectionView indexPathsForSelectedItems] indexOfObject:indexPath] != NSNotFound){
UIView *bgView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 250, 250)];
bgView.backgroundColor = kLightBlueColor;
[cell setSelectedBackgroundView:bgView];
cell.selected = YES;
}
cell.title.text = #"Hello World";
return cell;
}

Dynamically display an image in a UITableViewCell

I have a UITableViewCell, it is scroll-disabled and with fixed sections and rows.
There are two sections, with 1 row in section 0, and several rows in section 1.
The tableView is for users to make some choices.
So, the first section (with only one row) is going to display the result of users' choices,
and no doubt the second section (with several rows) is for choosing.
Now I want to put an image in the cell of the only row of the first section,
and this image will change according to users' choose.
It is very easy to judge which png image should be displaying, but I have trouble update it.
I tried use the cell's imageView, and manually alloc a UIImageView or UIView there to display those images.
But all of them won't work, I mean they just keep what they are displaying at the beginning and never changes it, even if I set the view's background or its image to a new png.
I tried some method like
[myImage setNeedsDisplay] for the manually alloced view,
or
[thatCell setNeedsDiaplsy] & [self.tableView reloadData] for the imageView of that cell,
but in vain.
So I wonder how can I achieve this function that dynamically display an image in a UITableViewCell in different situations?
Thanks a lot!
_____update line_____
I'm sorry that I didn't provide my code.
and here they are.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *inOutTableCellIdentifier = #"DealTableViewIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:inOutTableCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:inOutTableCellIdentifier] autorelease];
cell.textLabel.font = [UIFont fontWithName:cMyFont size:[UIFont systemFontSize]];
cell.detailTextLabel.font = [UIFont fontWithName:cMyFont size:[UIFont smallSystemFontSize]];
// I tried using both imageView and UIView here, but won't work
if (indexPath.section == 0) {
//cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"moneyCell.png"]];
cell.backgroundColor = [UIColor cyanColor];
// cell.imageView.image = [UIImage imageNamed:#"dealDone2.png"];
self.undoneIcon = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
//self.undoneIcon.image = [UIImage imageNamed:#"dealUndone2.png"];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"dealUndone2.png"]];
[cell.contentView addSubview:self.undoneIcon];
.... // deal with other rows in section 1
return cell;
}
// and this is the method that update the image, the images are named "dealDoneX.png",
// where X is an integer from 0 to 4.
- (void)checkUndoneDegree { // here I also tried 2 ways corresponding to UIView or a cell's imageView, but neither works well.
int i = 0;
if (self._date)
i++;
if (self.moneyTextField.text)
i++;
if (self._incomingAccount)
i++;
if (self._expensingAccount)
i++;
if (_dealType != kTransferView)
if (self._type)
i++;
NSLog(#"undone degree: %d",i);
NSString *imageName = [#"dealUndone" stringByAppendingFormat:#"%d",i];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageName]];
[self.undoneIcon setNeedsDisplay];
// NSUInteger p[2] = {0,0};
// UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:p length:2]];
// [cell setNeedsDisplay];
}
and everytime I update the table's data, like changing some text of some cell,
I would call [self checkUndoneDegree] and then call [self.tableView reloadData],
But the picture is never updated, at least from the screen.
I even tried to put the codes that decide which png to set in the
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
method, but it can only make the view displaying the first png, without any updating.
Thanks for helping!
Make your undoneDegree (represented by i variable in your code) an ivar of your view controller, so it is accessible in all of it's methods, also in the UITableView delegate and data source methods.
Forget about setNeedsDisplay method. Since you are using UITableView to display your content, you need to play by its rules. This means you should use reloadSections:withRowAnimation: method.
Check again if you need self.undoneIcon. I'm pretty sure that imageView property should do. That is if you really want to display an image in the cell. If you want to create some kind of progress bar by manipulating cell's background, then use backgroundView property, or just place UIProgressView in your cell. This is what I think your want to do, after looking at your code.

Resources