1

I have a feed that gets populated with 15 posts from the server. When I scroll down to 3 before the end of the list, I ping the server for the next 15 posts. This functionality works great. However, when I start scrolling up, the UITableViewCells frequently jump up, as though Cell 5 is now populating Cell 4, and Cell 4 is now populating Cell 3, etc. Either that, or the UITableView scroll is just jumping up.

When I get to the very top of the UITableView and then proceed to scroll down through all my data then back up, it works perfectly though. Is there a drawing issue with my table?

Edit: So, I've come across the understanding that this is happening because the heights of all my cells are dynamic. I'm pretty sure as I'm scrolling up, my UITableView is calculating and setting the appropriate heights, which is causing the jumpy action. I'm not sure how to mitigate that.

David
  • 7,028
  • 10
  • 48
  • 95
  • 3
    I wouldn't expect much help. Unfortunately these problems are nearly impossible to debug if you can't run the actual project. You have to sprinkle println() on all of your scrolling and tableDataSource methods and look at things like the contentOffset and the requested indexPaths of the table. Once you have figured out what exactly happens while the glitch happens you are halfway there. – Matthias Bauch Jan 30 '15 at 11:23
  • Just another suggestion if this is iOS8 - don't put a nested view in the cell's content view, but instead put your content directly in the content view. I had a very similar problem which I resolved in this way. – GuybrushThreepwood Jan 30 '15 at 11:47
  • @GuybrushThreepwood I'm not sure I understand what you mean – David Jan 30 '15 at 11:49
  • The contents of your cell. Are they in a view within the cell's content view ? – GuybrushThreepwood Jan 30 '15 at 11:50
  • The contents of my cell are directly within the ContentView, not a View. – David Jan 30 '15 at 11:52
  • So, when I set a constant for the heightForRowAtIndexPath method, I don't get any jumping activity within my cells on scrolling up. The problem is that, my posts are of dynamic height, and because they're dynamic, I expect that as I am scrolling up, my table is calculating the heights of all the posts and then taking them into consideration accordingly. This is problematic though because it looks very hacky. I wonder if there is a way to force reloadData() to calculate all of that beforehand. – David Jan 30 '15 at 12:13
  • http://stackoverflow.com/questions/26518306/uitableview-jumpy-on-scroll-after-changing-cell-height It seems I'm not the only one who has encountered this – David Jan 30 '15 at 12:20

2 Answers2

0

I never used the new funcionality of dynamic cell size in iOS8, but I can give you few suggestion for improve performance on table views. It should be a comment but it doesn't fit.

  • Cache the height of cells already displayed if you can. It's easy an dictionary paired with a sort of id would do the trick
  • Pay attention that you do not have complex layout between subviews of you cells
  • Check if you are drawing something that requires offscreen rendering, such as corner radius, clipping etc

I don't know how dynamic cell works on ios8 but I share piece of my code. It's pretty straightforward. I have a cell that I use as prototype, each times I need to calculate a cell height I feed it with my data, that I force it's layout to get me the correct height. Once I've got the height I saved it in a NSDictionary using the postID(it's a twitter like app) as a key.
This happens only when the cell height is not cached. If it is cached the height is returned.

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGSize  size = CGSizeZero;
    NSDictionary * data = self.timelineData[indexPath.row];
    if (data[KEY_CELL_IDENTIFIER] == CellIdentifierPost) {
        NSNumber * cachedHeight = [self.heightCaches objectForKey:@([(AFTimelinePostObject*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];
        if (cachedHeight) {
            return (CGFloat)[cachedHeight doubleValue];
        }
        [_heightCell configureCellWith:data[KEY_CELL_DATA]];
        size = [_heightCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        [self.heightCaches setObject:@(size.height) forKey:@([(AFTimelinePostObject*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];
    }
    else if (data[KEY_CELL_IDENTIFIER] == CellIdentifierComment){
        NSNumber * cachedHeight = [self.heightCaches objectForKey:@([(AFTimelinePostComments*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];
        if (cachedHeight) {
            return (CGFloat)[cachedHeight doubleValue];
        }
        [_heightCommentCell configureCellWith:data[KEY_CELL_DATA]];
        size = [_heightCommentCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        if (size.height < 80.0f) {
            size = (CGSize) {
                .width = NSIntegerMax,
                .height = 115.f
            };
        }
        else if (size.height > 180.0f) {
            size = (CGSize) {
                .width = NSIntegerMax,
                .height = 180.f
            };
        }

        [self.heightCaches setObject:@(size.height) forKey:@([(AFTimelinePostComments*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];

    }
    else {
        size = (CGSize) {
            .width = NSIntegerMax,
            .height = 50.f
        };
    }

    return size.height;
}
Andrea
  • 26,120
  • 10
  • 85
  • 131
  • I don't think caching will do anything. I'll still notice the effect. The issue stems from the fact that it uses my EstimatedHeight and then when it is time for it to be displayed on the screen, it quickly changes the height to what it should be. Granted, because I am infinite scrolling, I think that I shouldn't even bother reloading the data of any cells above the one at the bottom of my screen. Reloading data makes it recalculate, but I don't need it to do that. I only need it to calculate from my cell downwards. Perhaps there is a way to do that. – David Jan 30 '15 at 12:31
  • I updated my comment. Accidentally pressed enter prematurely. – David Jan 30 '15 at 12:34
  • I did an app that calculate dinamically cell heights based on their contents (with/out text with/out images) and caching height actually made a huge difference when you want to scroll back. It increases performance a lot. Due to mechanism of dequeuing every time you push data inside a cell is always a new data for it and it needs to get height anyway. It's like you have pool of cells when one goes out of screen enter in this pool, and when the t.v. need it pulls from there and change it's data according to indexpath. It doesn't matter infinite scrolling, but of course it can be a bug of iOS8. – Andrea Jan 30 '15 at 12:41
  • Where should I be calling the caching? and how? – David Jan 30 '15 at 12:44
0

Unfortunately, there does not seem to be a simple answer to this. I have struggled with it on multiple iOS apps.

The only solution I have found is to programmatically scroll to the top of your UITableView once it appears again.

[self.tableView setContentOffset:CGPointMake(0, 0 - self.tableView.contentInset.top) animated:YES];

OR

self.tableView.contentOffset = CGPointMake(0, 0 - self.tableView.contentInset.top);

Hope this an acceptable work around while still being able to use dynamic cell heights =)

BennyTheNerd
  • 3,930
  • 1
  • 21
  • 16