UITableViewCell content flickers when reloading cells separately

I have a table view related to NSFetchedResultController (i.e. loading data as well as tracking data changes linked to FRC)

I do not use AutoLayout in my cells (due to the huge performance AutoLayout that it introduces in iOS 8). I place the contents of the cells manually in the cells (using -(void)layoutSubviews ). plus, row heights are calculated based on the content and are correctly cached / invalid.

If any condition associated with my data changes, the associated cells are updated individually (using the implemented delegation methods of everything -(void)controllerWillChangeContent:... through -(void)controllerDidChangeContent:... ) animation for row updates: UITableViewRowAnimationNone

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

I tried many things to no avail!

Here is what I tried, which does not work:

  • Implementation -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath returning the exact value for the estimated height.
  • setting self.tableView.rowHeight=UITableViewAutomaticDimension; helps a lot, but doesn't fix it.
  • Disabling animation after [self.tableView beginUpdates]; and inclusion after [self.tableView endUpdates];
  • Removing all views from cell contents in -(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

Things that help a bit:

  • Make the cells opaque (i.e. set the background color for the cells). strange, artifacts appear to be under the actual contents of the cell.

More details

  • Most of the contents of my cell are nodes from AsyncDisplayKit (ASTextNode, ASImageNode), but replacing them with their UIKit counterparts Solve the problem.
  • All my visual updates happen in the main thread. I am always sure of that.
  • The problem is not universal, but exists more often than not. They seem to happen more after inserting the second cells, if that helps.
  • The method entry -(void)layoutSubviews in my cells shows that the cells with the update condition are reloaded 3 times in a row with each update, unlike other cells that are updated once. I do not force anyone [cell layoutIfNeeded]; or [cell setNeedsLayout]; .
  • Rebooting the entire table view using [self.tableView reloadData]; for me is not an option.

Additional Information

  • Creating cells opaque leads to the fact that cells are laid out only once! weird!

In the end, I'm sorry, I can’t share any actual code, because this is not a test project, and the complexity of the implementation makes any code exchange useless, unless it is understood in general.

Thank you for your time.

+4
source share
4 answers

If someone is facing the same problem:

If you calculate the height of your cell and its elements, and you depend on NSIndexPath to request any data or conditions, always check that your index path is valid (not nil ). Most UITableView methods that return an index path (for example, -(NSIndexPath *)indexPathForCell:.. etc.) can return nil values ​​and will shoot you in a spiral that debugs the views.

In my case, I had a way to determine if my cell should have a title, and for that I checked that this is the first cell or condition has changed from the previous cell. the problem was indexPath.row == 0 is true even if the pointer path is actually nil (this is a conceptual problem in Objective-C where nil is 0). so for a brief moment my cells thought he had a headline!

I feel stupid without checking the nil index paths, but I think sharing can help someone.

+5
source

I also had to face the UITableViewCell flashing problem when using reloadRowsAtIndexPath, so I did this -

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

In the above snippet there will be no animation on the cells when the cell is reloaded.

+15
source

In using it quickly and reducing flicker

 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) } 
+3
source

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

 UIView.performWithoutAnimation { self.tableView.reloadRows(at: [indexPath], with: .none) } 
0
source

All Articles