21

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];
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
DerrickHo328
  • 4,664
  • 7
  • 29
  • 50
  • What does your code for configuring the cells look like? – Paul.s Sep 29 '14 at 18:14
  • I discovered that this bug effects ios 8 but not ios 7 – DerrickHo328 Sep 29 '14 at 18:25
  • @Calimari328 Please answer the question you were asked. There's no iOS 8 bug here. This is something _you_ are doing wrong. If you want help, you must _show_ what you are doing. – matt Sep 29 '14 at 18:33
  • I have added code. The ones relevant to the uitableview and the scrolling. – DerrickHo328 Sep 29 '14 at 18:46
  • Does your tableview have a footer view attached? – isaac Sep 29 '14 at 18:46
  • @isaac no footer, no header is attached – DerrickHo328 Sep 29 '14 at 18:49
  • Got. Sorry, no answers for you. The reason I asked is that I had a bizarre scrolling malfunction on a tableview in iOS8. Removing the tableview's footer view (which had worked fine in iOS7) fixed the issue. – isaac Sep 29 '14 at 18:50
  • Did u find a solution? I also have that problem, but I noticed that my tableview jumps when scrolling back only after I did a reloadData (to add more data to the tableview) – rsc Feb 25 '15 at 21:31
  • I did not solve it but your observation might right. It is easier (I guess) to grow the tableview down and harder to grow it up. I guess when going up the default height is used and then later expands or contracts causing the misplace ment. Despite knowing this I have not found a solution. The issue seems to happen on views that are larger than the screen – DerrickHo328 Feb 25 '15 at 21:37

4 Answers4

5

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.

Community
  • 1
  • 1
WeichengChu
  • 116
  • 3
  • 3
    I actually have that delegate method implemented with a hard coded value. It is still jumping around. But maybe the bug is related to the heights being over written. I shall explore what happens when I adjust all the other methods related to height – DerrickHo328 Oct 03 '14 at 17:10
1

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)

eNeF
  • 3,241
  • 2
  • 18
  • 41
1

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.

Nick Yap
  • 887
  • 1
  • 10
  • 13
-1

You should calculate your row height yourself and you should remove everything about estimated row height in your code.

oso
  • 39
  • 6