12

I have a table view linked to a NSFetchedResultController (i.e. loading data and also tracking changes to data is bound to the FRC)

I'm not using AutoLayout in my cells (due to huge performance drop it introduces in iOS 8). I'm laying out my cells' content manually in cells (using -(void)layoutSubviews). plus, the height of rows are calculated based on content and are cached/invalidated properly.

If any condition related to my data changes, related cells get updated individually (with the whole -(void)controllerWillChangeContent:... through -(void)controllerDidChangeContent:... delegate methods implemented) the animation for row updates is: UITableViewRowAnimationNone

The problem here is that, My cells have transparent backgrounds, and I can see some visual noise (most likely the actual cell getting stretched vertically, I can't say for sure because they are really transient and short lived) during [self.tableView reloadRowsAtIndexPaths:...];.

I have tried many things to no avail!

Here are the things I have tried that doesn't work:

  • Implementing -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath, returning the accurate value for estimated heights.
  • setting self.tableView.rowHeight=UITableViewAutomaticDimension;, helps a lot but doesn't fix it.
  • Disabling animation upon [self.tableView beginUpdates]; and enabling after [self.tableView endUpdates];
  • Removing all subviews from cells' content in -(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

Things that help a little:

  • Making Cells opaque (i.e. setting a background color for cells). weirdly enough, the artifacts seems to be under the actual cell content.

More Info

  • Most of my cells' content are nodes from AsyncDisplayKit (ASTextNode, ASImageNode), but replacing them with their UIKit counterparts doesn't solve the problem.
  • All my visual updates are happening on main thread. I always make sure of that.
  • The problem is not ubiquitous, but exist more often than not. They seem to happen more after second cells inserted if it helps.
  • Logging the method -(void)layoutSubviews in my cells shows that the cells with update condition get reloaded 3 times in a row upon each update in contrast with other cells getting updated just the once. I'm not forcing any [cell layoutIfNeeded]; or [cell setNeedsLayout]; anywhere.
  • Reloading the whole table view using [self.tableView reloadData]; is not an option for me.

Additional More Info

  • Making cells opaque leads to cells getting laid out just the once! weird!

At the end, I'm sorry I can't share any actual code, because it's not a test project and the complexity of the implementations renders any code sharing futile unless comprehended as a whole.

Thanks for your time.

Community
  • 1
  • 1
M. Porooshani
  • 1,797
  • 5
  • 34
  • 42
  • Do you need to reload the cell instead of just updating the content? – Wain Apr 20 '15 at 06:05
  • Updating the content seems kind of off topic here. Following content change is something I'm avoiding. Instead of updating content (which by the way may need the table view to reload in order to resize the cell's height) – M. Porooshani Apr 20 '15 at 06:10
  • Not really off topic, seems exactly like the topic... Resizing the cells doesn't require reload either. Try updating the cell instead of reloading it. – Wain Apr 20 '15 at 06:13
  • OK, I seems to misunderstand you. care to elaborate? Also important: content height may change after data getting updated. – M. Porooshani Apr 20 '15 at 06:15

6 Answers6

24

I was also facing same problem of flickering UITableViewCell while using reloadRowsAtIndexPath, so what I did was -

     [UIView setAnimationsEnabled:NO];
     [self.tableView beginUpdates];

     [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
                                               withRowAnimation:UITableViewRowAnimationNone];
     [self.tableView endUpdates];
     [UIView setAnimationsEnabled:YES];

The above snippet will have no animation on cells while reloading the cell.

Sandy
  • 3,021
  • 1
  • 21
  • 29
  • 2
    dear @Sandy, I am aware of the solution above, but unfortunately my table view receives a lot more frequent updates and it is mixed with other types of updates (insert, delete, move), I don't want to lose other animations well. plus, my problem had nothing to do with animations per se, it was a mistake on my part. Thanks for your time. – M. Porooshani May 15 '15 at 04:07
  • You, Sandy, are a Saint. – Bob Spryn Jan 23 '18 at 20:41
19

Yeah, you can achieve a similar result from the @Sandy's answer using performWithoutAnimation (Swift Version)

UIView.performWithoutAnimation {
      self.tableView.reloadRows(at: [indexPath], with: .none)
}
Antonio Junior
  • 469
  • 6
  • 13
6

If anyone encountered the same problem:

If you are calculating your cell height and its elements and you are dependent on NSIndexPath for querying any data or conditions, always check that your index path is valid (not nil). Most of UITableView methods that return index path (e.g -(NSIndexPath *)indexPathForCell:.., etc.) may return nil values and will shoot you down an spiral that is debugging views.

In my case, I had a method to determined if my cell should have a header, and for that I checked wether it is the first cell or a condition has changed since previous cell. the problem was indexPath.row == 0 is true even if the index path is actually nil (it's a conceptual problem in Objective-C where nil is 0). so for a brief moment my cells would thought it has a header!

I feel kind of stupid not checking for nil index paths but I think sharing it might help somebody someday.

M. Porooshani
  • 1,797
  • 5
  • 34
  • 42
3

In swift am using this and it decreases the flickering

let visibleIndexs = self.ContentTableview.indexPathsForVisibleRows! as NSArray
        print(visibleIndexs)
        for indx in visibleIndexs {
            if self.imgDownloadedIndexs!.containsObject(indx) {
                  UIView.setAnimationsEnabled(false)
                self.ContentTableview.beginUpdates()
                self.ContentTableview.reloadRowsAtIndexPaths([indx as! NSIndexPath], withRowAnimation:.None)
                self.ContentTableview.endUpdates()
                 UIView.setAnimationsEnabled(true)
                self.imgDownloadedIndexs?.removeObject(indx)

            }
rsjaffe
  • 5,600
  • 7
  • 27
  • 39
Subin
  • 114
  • 5
  • When you set the UITableViewRowAnimation to .none on the reloadRowsAtIndexPaths you don't need to disable the animations via setAnimationsEnabled(). – Leon Aug 15 '17 at 09:23
0

Removing rowHeightAtIndexPath method will solve this Issue. make sure you have set some fixed height to the table cell in the story board.

Santosh
  • 1,275
  • 1
  • 10
  • 21
-1

The tableView custom cell was not tappable and flickering. It was because of the cell animation. Check the Tableview delegate methods for access to the database.

This case occurs normally and can also occur at the time of running UITestCase.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }

Panky
  • 1
  • 2