简体   繁体   English

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

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

I am using iOS 8 new self-sizing cells.我正在使用 iOS 8 新的自适应单元格。 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?这是一个错误还是有解决方法?

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 scrollToRowAtIndexPath function, 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谢谢

Update: Jun 24, 2015更新:2015 年 6 月 24 日

Apple has addressed most of these bugs as of the iOS 9.0 SDK.从 iOS 9.0 SDK 开始,Apple 已经解决了大部分这些错误。 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 reloadData while scrolled in the middle of the table view.从 iOS 9 beta 2 开始,所有问题都已修复,包括在没有动画的情况下滚动到 table view 的顶部和底部,以及在 table view 中间滚动时调用reloadData

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).当使用较小的估计行高时,滚动到带有动画的最后一行会导致 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 reloadData while scrolled partially or fully down (eg contentOffset.y is 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.我于 2013 年 10 月 21 日将此问题作为 Radar #15283329 提交。 Please do file duplicate bug reports so that Apple prioritizes a fix.请提交重复的错误报告,以便 Apple 优先修复。

You can attach this simple sample project to demonstrate the issue.您可以附上这个简单的示例项目来演示该问题。 It is based directly on Apple's own sample code.它直接基于 Apple 自己的示例代码。

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.请告诉我这是否也适用于您。

It is definitely a bug from Apple.这绝对是Apple的错误。 I also have this problem.我也有这个问题。 I solved this problem by calling "scrollToRowAtIndexPath" method twice example code is:我通过两次调用“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)
        })
    }

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 viewDidLayoutSubviews and it finds the correct place on the bottom (or a very close estimated position).viewDidLayoutSubviews ,它会在底部(或非常接近的估计位置)找到正确的位置。

I hope that helps.我希望这有帮助。

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!希望能帮助到你!

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)
 }

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.我正在编写一个聊天表视图,所以很可能我的许多单元格,特别是文本类型将大于 IB 中的文本类型,特别是如果聊天消息很长。 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随着滚动的时间变长,它似乎会变得更糟,但我想这是可以预料的

Just call tableview reloadData after viewDidAppear can solve the problem viewDidAppear后调用tableview reloadData即可解决问题

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

Although as smileyborg's answer it 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.要在 iOS9 之前的版本上变通,下面的代码在没有任何 dispatch_async 或 dispatch_after 的情况下完成了这个技巧。 Tested on iOS 8.4 simulator.在 iOS 8.4 模拟器上测试。

UPDATE : Calling (only) layoutIfNeeded does not work when view controller become visible by UIPageViewController being scrolled.更新:当视图控制器因 UIPageViewController 滚动而变得可见时,调用(仅)layoutIfNeeded 不起作用。 So use layoutSubviews (or maybe setNeedsLayout + layoutIfNeeded) instead.因此,请改用 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);
}

I had the same problem when creating a chat tableView with different height of cells.在创建具有不同高度单元格的聊天 tableView 时,我遇到了同样的问题。 I call the code below in viewDidAppear() lifecycle method:我在 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.如果这对你也有用,请告诉我。

From the storyboard window click in a blank area to deselect all views then click the view that has the table view in it and then click the Resolve Auto Layout Issue icon and select Reset to Suggested Constraints在故事板窗口中单击空白区域以取消选择所有视图,然后单击包含表视图的视图,然后单击Resolve Auto Layout Issue图标并选择Reset to Suggested Constraints

在此处输入图片说明在此处输入图片说明

Use this simple code to scroll bottom使用这个简单的代码滚动底部

 var rows:NSInteger=self.tableName.numberOfRowsInSection(0)
        if(rows > 0)
        {
            let indexPath = NSIndexPath(forRow: rows-1, inSection: 0)
            tableName.scrollToRowAtIndexPath(indexPath , atScrollPosition:  UITableViewScrollPosition.Bottom, animated: true)
        }
            }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 iOS 8自动高度单元可以工作,但是当我向上滚动时会跳 - IOS 8 auto height cell works but when i scroll up it jumps UITableView无法滚动到最后一行 - UITableView Can't scroll to last Row iOS-自动调整单元格高度和标签高度 - iOS - Auto resize cell height & label height iOS 7/8 UITableView Cell:两个UILabel,动态高度,自动布局,可变行高 - iOS 7/8 UITableView Cell: Two UILabels with dynamic height with auto layout for variable row height 如何在iOS中调整单元格高度和行高度 - How to resize cell height and the row height in ios 自动大小表格视图中嵌套的水平collectionview单元格的扩展高度使行重新加载单元格时使collection view滚动重置 - nested horizontal collectionview in auto size tableview cell exanding height make collection view scroll reset when reload cell for row accured iOS 7 Safari无法滚动带有溢出的DIV:隐藏或自动 - iOS 7 Safari can't scroll DIV with overflow: hidden or auto ios - 无法使UITableView单元格高度与文本量成比例 - ios - can't get the UITableView cell height to be proportional to the amount of text iOS 8自动高度单元在第一次加载时不正确的高度 - iOS 8 Auto height cell not correct height at first load 指定错误单元格的行的高度-iOS - Height for row appointed to wrong Cell - iOS
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM