I have an odd situation with my UItableView.
I am loading images from the internet and presenting them in a uitableviewcell in an asynchronous manner. When I scroll down (i.e. row 1 to 6) the scroll scrolls down smoothly just as I expect it to.
However, then I scroll up (i.g. row 6 to 1) the scroll jumps back. for example if I scroll from row 6 to row 5, it will jump back to row 6. The second time I try to scroll up it lets me go up to row 4, but then it scrolls me back to row 5 or 6 but usually just 5.
What I don't understand is why this is happening in only one direction.
It seems to be effecting ios 8 but not ios 7.
Therefore it is an implementation difference in how ios8 and 7 handle the uitableview.
How can I fix this?
Here is some code to give you some context
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CVFullImageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
CVComicRecord *comicRecord = self.comicRecords[indexPath.row];
[cell.loaderGear stopAnimating];
[cell.text setText:comicRecord.title];
NSString *UUID = comicRecord.fullImagePageURL.absoluteString;
[cell setUUID:UUID];
//Check if image is in the cache
UIImage *fullImage = [self.contentViewCache objectForKey:UUID];
[cell setComicFullImage:fullImage];
if (fullImage) {
return cell;
}
[cell.loaderGear startAnimating];
[self requestImageForIndexPath:indexPath];
return cell;
}
- (void)requestImageForIndexPath:(NSIndexPath *)indexPath {
CVComicRecord *comicRecord = self.comicRecords[indexPath.row];
NSString *UUID = comicRecord.fullImagePageURL.absoluteString;
if ([self.contentViewCache objectForKey:UUID]) {
//if it is already cached, I do not need to make a request.
return;
}
id fd = CVPendingOperations.sharedInstance.fullDownloadersInProgress[UUID];
if (fd) {
//if it is in the queue you do no need to make a request
return;
}
comicRecord.failedFull = NO;
CVFullImageDownloader *downloader = [[CVFullImageDownloader alloc] initWithComicRecord:comicRecord withUUID:UUID];
[CVPendingOperations.sharedInstance.fullDownloaderOperationQueue addOperation:downloader];
//when operation completes it will post a notification that will trigger an observer to call fullImageDidFinishDownloading
}
- (void)fullImageDidFinishDownloading:(NSNotification *)notification {
CVComicRecord *comicRecord = notification.userInfo[@"comicRecord"];
NSString *UUID = notification.userInfo[@"UUID"];
UIImage *fullImage = notification.userInfo[@"fullImage"];
comicRecord.failedFull = NO;
[self.contentViewCache setObject:fullImage forKey:UUID];
dispatch_async(dispatch_get_main_queue(), ^{
for (NSIndexPath *indexPath in [self.tableView indexPathsForVisibleRows]) {
CVFullImageTableViewCell *cell = (id)[self.tableView cellForRowAtIndexPath:indexPath];
if (cell) {
if ([cell.UUID isEqualToString:UUID]) {
[cell.loaderGear stopAnimating];
[cell setComicFullImage:fullImage];
[cell layoutIfNeeded];
}
}
}
});
}
#pragma mark - Scroll
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
static int oldOffset = 0;
int newOffset = scrollView.contentOffset.y;
int dy = newOffset- oldOffset;
if (dy > 0) {
[self hideNavigationbar:YES animationDuration:0.5];
} else if (dy < 0) {
[self hideNavigationbar:NO animationDuration:0.5];
}
oldOffset = newOffset;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (decelerate == NO) {
[self prioritizeVisisbleCells];
//currentPage is a property that stores that last row that the user has seen
[self setCurrentPage:[self currentlyViewedComicIndexPath].row];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self prioritizeVisisbleCells];
//currentPage is a property that stores that last row that the user has seen
[self setCurrentPage:[self currentlyViewedComicIndexPath].row];
}
- (void)prioritizeVisisbleCells {
NSArray *ips = [self.tableView indexPathsForVisibleRows];
NSArray *activeIndexPaths = [CVPendingOperations.sharedInstance.fullDownloadersInProgress allKeys];
//add visible cells to queue first
NSSet *visible = [NSSet setWithArray:ips];
NSMutableSet *invisible = [NSMutableSet setWithArray:activeIndexPaths];
[invisible minusSet:visible];
for (NSIndexPath *ip in invisible) {
NSOperation *op = CVPendingOperations.sharedInstance.fullDownloadersInProgress[ip];
[op setQueuePriority:NSOperationQueuePriorityNormal];
}
for (NSIndexPath *ip in visible) {
NSOperation *op = CVPendingOperations.sharedInstance.fullDownloadersInProgress[ip];
[op setQueuePriority:NSOperationQueuePriorityHigh];
}
}
I found the answer in this post very helpful: Table View Cells jump when selected on iOS 8
Try to implement the tableView:estimatedHeightForRowAtIndexPath:
in the UITableViewDelegate protocol. It works for me.
Implement the self-sizing and correct view constraints in your storyboard.
in your code put this together
tableView.rowHeight = UITableViewAutomaticDimension; tableView.estimatedRowHeight = CGFloat value (the initial value)
This exact problem was happening to me while I was using the UITableViewAutomaticDimension
and the estimatedRowHeight
, as @nferocious76 suggested.
The issue was that my estimatedRowHeight
was 400.0 while the actual row height was more like 80.0 (I copy/pasted it from another table view in my app with large cells). I never bothered to change it because I had read somewhere that the estimatedRowHeight
didn't really matter as long as it was greater than the actual row height, but apparently this is not the case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With