【问题标题】:ios8 UITableView scrolling jumps back when scrolling upios8 UITableView滚动向上滚动时跳回
【发布时间】:2026-01-06 06:50:02
【问题描述】:

我的 UItableView 有一个奇怪的情况。

我正在从 Internet 加载图像并以异步方式将它们呈现在 uitableviewcell 中。当我向下滚动(即第 1 到第 6 行)时,滚动会像我预期的那样平滑向下滚动。

但是,然后我向上滚动(例如,从第 6 行到第 1 行)滚动会跳回来。例如,如果我从第 6 行滚动到第 5 行,它将跳回第 6 行。我第二次尝试向上滚动时,它让我向上滚动到第 4 行,但随后又将我滚动回第 5 行或第 6 行,但通常只有 5 个。

我不明白为什么这只发生在一个方向。

它似乎影响 ios 8 但不影响 ios 7。

因此,ios8 和 7 处理 uitableview 的方式存在实现差异。

我该如何解决这个问题?


这里有一些代码可以给你一些上下文

- (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];
    }
}

【问题讨论】:

  • 配置单元格的代码是什么样的?
  • 我发现这个 bug 影响 ios 8 而不是 ios 7
  • @Calimari328 请回答您被问到的问题。这里没有 iOS 8 错误。这是做错了。如果你需要帮助,你必须展示你在做什么。
  • 我已经添加了代码。与 uitableview 和滚动相关的那些。
  • 您的 tableview 是否附加了页脚视图?

标签: ios objective-c uitableview ios7 ios8


【解决方案1】:

我发现这篇文章中的答案很有帮助:Table View Cells jump when selected on iOS 8

尝试在 UITableViewDelegate 协议中实现tableView:estimatedHeightForRowAtIndexPath:。它对我有用。

【讨论】:

  • 我实际上已经使用硬编码值实现了该委托方法。它还在跳来跳去。但也许这个错误与被覆盖的高度有关。我将探索当我调整所有其他与高度相关的方法时会发生什么
【解决方案2】:

在情节提要中实现自定大小和正确的视图约束。

在你的代码中把它放在一起

tableView.rowHeight = UITableViewAutomaticDimension; tableView.estimatedRowHeight = CGFloat值(初始值)

【讨论】:

    【解决方案3】:

    正如@nferocious76 所建议的那样,当我使用UITableViewAutomaticDimensionestimatedRowHeight 时,这个确切的问题发生在我身上。

    问题是我的estimatedRowHeight 是 400.0,而实际行高更像是 80.0(我从我的应用程序的另一个表视图中复制/粘贴了它,其中包含大单元格)。我从来没有费心去改变它,因为我在某处读到 estimatedRowHeight 并不重要,只要它大于实际行高,但显然情况并非如此。

    【讨论】:

      【解决方案4】:

      您应该自己计算行高,并且应该删除代码中有关估计行高的所有内容。

      【讨论】: