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

2020-01-25 15:36发布

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?

Here's how to recreate the problem:

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

However, if I am calling the scrollToRowAtIndexPath function, like this:

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.

Would appreciate any help.

Thanks

11条回答
太酷不给撩
2楼-- · 2020-01-25 16:19

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

        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)
        })
    }
查看更多
Fickle 薄情
3楼-- · 2020-01-25 16:22

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

查看更多
迷人小祖宗
4楼-- · 2020-01-25 16:23

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

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

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

// 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);
}
查看更多
萌系小妹纸
5楼-- · 2020-01-25 16:23

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)
        }
            }
查看更多
何必那么认真
6楼-- · 2020-01-25 16:28

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!

查看更多
登录 后发表回答