values changed when UITableview is scrolled up or down

I have a UITableview and it has some History in it. Im using Custom Cell to handle the display of Labels, Images and buttons. There are around 50 rows inside the UITableview. But problem is when the Tableview is scrolled up, then the row below the first displayed ones have the values changed in it. Not sure what exactly is happening with the Tableview.

static NSString *CellIdentifier = @"HistoryCell";
cellHistory = [self.notificationTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

[cellHistory.label clearActionDictionary];
//Step 2: Define a selection handler block
void(^handler)(FRHyperLabel *label, NSString *substring) = ^(FRHyperLabel *label, NSString *substring)
{
    [self userNameTapped:[[[[self.arrayHistory objectAtIndex:indexPath.row] objectForKey:Details] objectForKey:@"key"] objectForKey:@"key"]];
};

//Step 3: Add link substrings
[cellHistory.label setLinksForSubstrings:@[userName] withLinkHandler:handler];
return cellHistory;

Answers


Beware

The following answer addresses the specific implementation, not the design. Using boilerplate code for NSFetchedResultsController would prevent this bug to occur in the first place. (Apple documentation). At all costs, avoid manipulating a cell once returned by cellForRowAtIndexPath: you do not own it.


1. Reused Cells are not cached

Of particular worry is the line:

[self.arrayHistory objectAtIndex:indexPath.row]

not so much because of that particular instance, but because it leads me to think that you are making assumptions elsewhere in this class: a UITableViewCell retrieved via dequeueReusableCellWithIdentifier is nothing but a transient object, to be used purely but the UITableView for display.

The very same cell instance will have multiple indexPath over time: such a cell may not be cached or manipulated at a further date. Once returned as the last statement of cellForRowAtIndexPath, it may not be accessed again, whether retained by an external pointer of through some asynchronous method. Always consider the index path of a cached table view obtained via dequeueReusableCellWithIdentifier as stale.

Do not modify the content of a cell once returned. Follow proper design as outlined by Losiowaty below.

Against better judgement, if you must modify the content of a cell already returned to the table view, then you must dedicate a persistent cell for that, using, for example, a dictionary of UITableViewCell. You may not invoke dequeueReusableCellWithIdentifier on such a cell unless that identifier is dedicated to that very cell ; while highly memory inefficient for large data sets, it will guarantee that cell are not re-used.


2. Do not guess the tableview

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    cellHistory = [self.notificationTableView // etc.

The proper logic in UITableViewDataSource cellForRowAtIndexPath is to use the parameter passed to you:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    cellHistory = [tableView // etc.

3. Reuse clean recycled cells

Assuming you have mastered the recycling side effects of using dequeueReusableCellWithIdentifier, and ensured that you are not cross pollinate different type of cells, you may want to cleanup your reused cell (credit to Timur Bernikowich for his comment):

func prepareForReuse() {
    super.prepareForReuse()
    // reset attributes of the cell that are not related to content
}

4. Introspect first

Always prefer the tack of what am I doing wrong to it used to work in the previous OS. While the second proposition is often true, by and large, history lets you ponder on how in heaven it used to work in the first place.

To the contrary, I generally take these situations as a sure sign I was doing something horribly wrong, and I was just lucky to fall through the cracks.


5. Respect naming conventions

While I admire your passing of blocks in the method

- (void)setLinksForSubstrings:(NSArray*)string withLinkHandler:(void(^)(FRHyperLabel *label, NSString *substring))handler {}

I was a bit thrown off by nature of the Details object or the userName, not to mention the 4 brackets in [self userNameTapped:[[[[. If I am thrown off, chances are other engineers will, when first being exposed to your code.

From the context, I cannot derive what are variables, constants, statics, instance properties, and so on. It is quite possible your cells are being confused at that level as well (you may be reusing values you assume being locals or stack based, but aren't).

May I suggest using a typedef for the block, adopt capitalization consistency, and refrain from nesting [ too deep. It took a non trivial effort to compare your code with boilerplate UITableView code, only to trim out the noise.


Need Your Help

Compiler optimizations on case statement

string compiler-optimization case-statement

I would like to broaden my knowledge and skills in compiler writing, especially optimizations. I would like to know what optimizations are available for case-statements with case expression of string

How to disable hostname verification or allow all certificates when using maven-jaxb2-plugin?

jaxb xjc maven-jaxb2-plugin

Currently, I'm using the maven-jaxb2-plugin to generate Java artefacts while consuming a soap web services via SSL. I configured my pom.xml per the answer here. But the certificate I used didn't co...