iOS 8 自动单元格高度 - 无法滚动到最后一行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25686490/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:14:13  来源:igfitidea点击:

iOS 8 Auto cell height - Can't scroll to last row

iosuitableviewswiftios8

提问by BenB

I am using iOS 8 new self-sizing cells. Visually it works good - each cell gets its right size. However, if I try to scroll to the last row, the table view doesn't seem to know its right size. Is this a bug or is there a fix for that?

我正在使用 iOS 8 新的自适应单元格。从视觉上看它效果很好 - 每个单元格都有合适的大小。但是,如果我尝试滚动到最后一行,表格视图似乎不知道它的正确大小。这是一个错误还是有解决方法?

Here's how to recreate the problem:

以下是重现问题的方法:

Using this project - TableViewCellWithAutoLayoutiOS8(referenced from this SO answer), I got the auto-resizing cells as expected.

使用这个项目 - TableViewCellWithAutoLayoutiOS8(从这个 SO 答案中引用),我按预期获得了自动调整大小的单元格。

However, if I am calling the scrollToRowAtIndexPathfunction, like this:

但是,如果我调用scrollToRowAtIndexPath函数,如下所示:

tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)

I do not get to the last row- It only gets me around halfway there.

没有到达最后一排- 它只能让我走到一半。

Even by trying to use a lower level function like this:

即使尝试使用这样的低级函数:

tableView.setContentOffset(CGPointMake(0, tableView.contentSize.height - tableView.frame.size.height), animated: true)

The result is not as expected, it won't get to the end. If I click it a lot of times or wait a few moments, eventually it will get to the right place. It seems the tableView.contentSize.height is not set correctly, so the iOS "doesn't know" where that last cell is.

结果并不如预期,它不会走到最后。如果我多次单击它或稍等片刻,它最终会到达正确的位置。似乎 tableView.contentSize.height 设置不正确,所以 iOS “不知道”最后一个单元格在哪里。

Would appreciate any help.

将不胜感激任何帮助。

Thanks

谢谢

采纳答案by smileyborg

Update: Jun 24, 2015

更新:2015 年 6 月 24 日

Apple has addressed most of these bugs as of the iOS 9.0 SDK. All of the issues are fixed as of iOS 9 beta 2, including scrolling to the top & bottom of the table view without animation, and calling reloadDatawhile scrolled in the middle of the table view.

从 iOS 9.0 SDK 开始,Apple 已经解决了大部分这些错误。从 iOS 9 beta 2 开始,所有问题都已修复,包括在没有动画的情况下滚动到 table view 的顶部和底部,以及在 table viewreloadData中间滚动时调用。

Here are the remaining issues that have not been fixed yet:

以下是尚未修复的其余问题:

  1. When using a large estimated row height, scrolling to the last row with animation causes the table view cells to disappear.
  2. When using a small estimated row height, scrolling to the last row with animation causes the table view to finish scrolling too early, leaving some cells below the visible area (and the last row still offscreen).
  1. 当使用较大的估计行高时,滚动到带有动画的最后一行会导致表格视图单元格消失。
  2. 当使用较小的估计行高时,滚动到带有动画的最后一行会导致 table view 过早完成滚动,从而在可见区域下方留下一些单元格(并且最后一行仍然在屏幕外)。

A new bug report (rdar://21539211) has been filed for these issues relating to scrolling with animation.

已针对这些与滚动动画相关的问题提交了新的错误报告 (rdar://21539211)。

Original Answer

原答案

This is an Apple bug with the table view row height estimation, and it has existed since this functionality first was introduced in iOS 7. I have worked directly with Apple UIKit engineers and developer evangelists on this issue -- they have acknowledged that it is a bug, but do not have any reliable workaround (short of disabling row height estimation), and did not seem particularly interested in fixing it.

这是 Apple 的表视图行高估计错误,自从 iOS 7 中首次引入此功能以来,它就一直存在。我直接与 Apple UIKit 工程师和开发人员就这个问题进行了合作——他们承认这是一个bug,但没有任何可靠的解决方法(缺少禁用行高估计),并且似乎对修复它没有特别兴趣。

Note that the bug manifests itself in other ways, such as disappearing table view cells when you call reloadDatawhile scrolled partially or fully down (e.g. contentOffset.yis significantly greater than 0).

请注意,该错误以其他方式表现出来,例如当您reloadData在部分或完全向下滚动(例如contentOffset.y显着大于 0)时调用时表格视图单元格消失。

Clearly, with iOS 8 self sizing cells, row height estimation is critically important, so Apple really needs to address this ASAP.

显然,对于 iOS 8 自动调整单元格,行高估计至关重要,因此 Apple 确实需要尽快解决这个问题。

I filed this issue back on Oct 21 2013 as Radar #15283329. Please do file duplicate bug reports so that Apple prioritizes a fix.

我于 2013 年 10 月 21 日将此问题作为 Radar #15283329 提交。请提交重复的错误报告,以便 Apple 优先修复。

You can attach this simple sample projectto demonstrate the issue. It is based directly on Apple's own sample code.

您可以附上这个简单的示例项目来演示该问题。它直接基于 Apple 自己的示例代码。

回答by abinop

This has been a very annoying bug, but I think I found a permanent solution, though I cannot fully explain why.

这是一个非常烦人的错误,但我想我找到了一个永久的解决方案,尽管我无法完全解释原因。

Call the function after a tiny (unnoticed) delay:

在微小的(未被注意的)延迟后调用该函数:

let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

dispatch_after(time, dispatch_get_main_queue(), {
  tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)
})

Do tell me if this works for you as well.

请告诉我这是否也适用于您。

回答by Bhavesh Tiwari

It is definitely a bug from Apple. I also have this problem. I solved this problem by calling "scrollToRowAtIndexPath" method twice example code is:

这绝对是Apple的错误。我也有这个问题。我通过两次调用“scrollToRowAtIndexPath”方法解决了这个问题,示例代码是:

        if array.count > 0 {
        let indexPath: NSIndexPath = NSIndexPath(forRow: array.count - 1, inSection: 0)
        self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
        let delay = 0.1 * Double(NSEC_PER_SEC)
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

        dispatch_after(time, dispatch_get_main_queue(), {
            self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
        })
    }

回答by Gui Moura

I found a temporary workaround that might be helpful until Apple decides to fixes the many bugs that have been plaguing us.

我找到了一个临时解决方法,在 Apple 决定修复困扰我们的许多错误之前,它可能会有所帮助。

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *text = [self findTextForIndexPath:indexPath];
    UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:13];
    CGRect estimatedHeight = [text boundingRectWithSize:CGSizeMake(215, MAXFLOAT)
                                                options:NSStringDrawingUsesLineFragmentOrigin
                                             attributes:@{NSFontAttributeName: font}
                                                context:nil];
    return TOP_PADDING + CGRectGetHeight(estimatedHeight) + BOTTOM_PADDING;
}

This is not perfect, but it did the job for me. Now I can call:

这并不完美,但它为我完成了工作。现在我可以调用:

- (void)scrollToLastestSeenMessageAnimated:(BOOL)animated
{
    NSInteger count = [self tableView:self.tableView numberOfRowsInSection:0];
    if (count > 0) {
        NSInteger lastPos = MAX(0, count-1);
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:lastPos inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

On viewDidLayoutSubviewsand it finds the correct place on the bottom (or a very close estimated position).

On viewDidLayoutSubviews,它会在底部找到正确的位置(或非常接近的估计位置)。

I hope that helps.

我希望这有帮助。

回答by Marcus

For my case, I found a temporary workaround by not suggesting an estimated cell height to the program. I did this by commenting out the following method in my code:

就我而言,我找到了一种临时解决方法,即不向程序建议估计的单元格高度。我通过在我的代码中注释掉以下方法来做到这一点:

- (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

However, please take note that doing so may affect the user experience when the user scrolls, if your cells varies a lot compared to each other. For my case, no noticeable difference so far.

但是,请注意,如果您的单元格彼此之间差异很大,这样做可能会影响用户滚动时的用户体验。就我而言,到目前为止没有明显差异。

Hope it helps!

希望能帮助到你!

回答by Hamed

I have this problem in Swift 5 iOS 13 yet, this solved my problem

我在 Swift 5 iOS 13 中有这个问题,这解决了我的问题

 DispatchQueue.main.async { [weak self] in
    self?.tableView.reloadData()
    self?.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
 }

回答by ypresto

Although as smileyborg's answerit is bug in iOS 8.x, it should be fixed in all platforms which you supports...

尽管Smileyborg 的回答是 iOS 8.x 中的错误,但它应该在您支持的所有平台中得到修复......

To workaround on pre-iOS9, below code do the trick without any dispatch_async or dispatch_after. Tested on iOS 8.4 simulator.

要在 iOS9 之前的版本上变通,下面的代码在没有任何 dispatch_async 或 dispatch_after 的情况下完成了这个技巧。在 iOS 8.4 模拟器上测试。

UPDATE: Calling (only) layoutIfNeeded does not work when view controller become visible by UIPageViewController being scrolled. So use layoutSubviews (or maybe setNeedsLayout + layoutIfNeeded) instead.

更新:当视图控制器因 UIPageViewController 滚动而变得可见时,调用(仅)layoutIfNeeded 不起作用。因此,请改用 layoutSubviews(或 setNeedsLayout + layoutIfNeeded)。

// For iOS 8 bug workaround.
// See https://stackoverflow.com/a/33515872/1474113
- (void)scrollToBottomForPreiOS9
{
    CGFloat originalY, scrolledY;
    do {
        // Lay out visible cells immediately for current contentOffset.
        // NOTE: layoutIfNeeded does not work when hosting UIPageViewController is dragged.
        [self.tableView layoutSubviews];
        originalY = self.tableView.contentOffset.y;
        [self scrollToBottom];  // Call -scrollToRowAtIndexPath as usual.
        scrolledY = self.tableView.contentOffset.y;
    } while (scrolledY > originalY);
}

回答by Stefan Olaru

I had the same problem when creating a chat tableView with different height of cells. I call the code below in viewDidAppear() lifecycle method:

在创建具有不同高度单元格的聊天 tableView 时,我遇到了同样的问题。我在 viewDidAppear() 生命周期方法中调用下面的代码:

// First figure out how many sections there are
let lastSectionIndex = self.tableView.numberOfSections - 1

// Then grab the number of rows in the last section
let lastRowIndex = self.tableView.numberOfRowsInSection(lastSectionIndex) - 1

// Now just construct the index path
let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)

// Make the last row visible
self.tableView.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)

Please let me know if that worked for you too.

如果这对你也有用,请告诉我。

回答by Roderic Campbell

My solution was to use the size of the storyboard as the estimate.

我的解决方案是使用故事板的大小作为估计。

So instead of this:

所以而不是这个:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {   
return UITableViewAutomaticDimension;

}

}

I did something like this:

我做了这样的事情:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 

MyMessageType messageType = [self messageTypeForRowAtIndexPath:indexPath];

switch (messageType) {

    case MyMessageTypeText:
        return 45;
        break;

    case MyMessageTypeMaybeWithSomeMediaOrSomethingBiggerThanJustText:
        return 96;
        break;

    default:
        break;
 }
}

I'm writing a chat table view so it is likely that many of my cells, specifically that text type will be larger than what is in IB, especially if the chat message is very long. This seems to be a pretty good...well...estimate and scrolling to the bottom gets pretty close. It seems to be slightly worse as the scrolling gets longer, but that is to be expected I suppose

我正在编写一个聊天表视图,所以很可能我的许多单元格,特别是文本类型将大于 IB 中的文本类型,特别是如果聊天消息很长。这似乎是一个很好的...嗯...估计和滚动到底部非常接近。随着滚动的时间变长,它似乎会变得更糟,但我想这是可以预料的

回答by Evan JIANG

Just call tableview reloadData after viewDidAppear can solve the problem

viewDidAppear后调用tableview reloadData即可解决问题

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}