Высота ячеек iOS 8 - не может прокручиваться до последней строки

Я использую новые ячейки для самостоятельной калибровки iOS 8. Визуально он работает хорошо - каждая ячейка получает нужный размер. Однако, если я попробую прокрутить до последней строки, представление таблицы, похоже, не знает его правильного размера. Это ошибка или есть исправление для этого?

Здесь, как воссоздать проблему:

Используя этот проект - TableViewCellWithAutoLayoutiOS8 (ссылка из этого SO-ответа), Я получил ячейки для автоматического изменения размеров, как и ожидалось.

Однако, если я вызываю функцию scrollToRowAtIndexPath, например:

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

Я не добираюсь до последней строки. Меня это доставит только на полпути.

Даже при попытке использовать функцию нижнего уровня, например:

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

Результат не так, как ожидалось, он не дойдет до конца. Если я нажму на него много раз или подожду несколько минут, в конце концов он попадет в нужное место. Кажется, что tableView.contentSize.height настроен неправильно, поэтому iOS "не знает", где находится последняя ячейка.

Поблагодарили бы за любую помощь.

Спасибо

Ответ 1

Обновление: 24 июня 2015 г.

Apple обратилась к большинству этих ошибок с IOS 9.0 SDK. Все проблемы исправлены с iOS 9 beta 2, включая прокрутку вверх и вниз по представлению таблицы без анимации и вызов reloadData во время прокрутки в середине представления таблицы.

Вот оставшиеся проблемы, которые еще не исправлены:

  • При использовании большой расчетной высоты строки прокрутка до последней строки с анимацией приводит к исчезновению ячеек таблицы.
  • При использовании небольшой высоты подсвеченной строки прокрутка до последней строки с анимацией приводит к тому, что представление таблицы заканчивает прокрутку слишком рано, оставляя некоторые ячейки ниже видимой области (и последней строки все еще вне экрана).

Для этих проблем, связанных с прокруткой с анимацией, был отправлен новый отчет об ошибке (rdar://21539211).

Оригинальный ответ

Это ошибка Apple с оценкой высоты строки таблицы таблицы, и она существует с тех пор, как эта функциональность была впервые представлена ​​в iOS 7. Я работал с инженерами Apple UIKit и разработчиками-евангелистами по этой проблеме - они признали, что это ошибка, но у нее нет надежного обходного пути (не считая отключения оценки высоты строки), и, похоже, не было особо заинтересовано в ее исправлении.

Обратите внимание, что ошибка проявляется другими способами, такими как исчезающие ячейки представления таблицы, когда вы вызываете reloadData при прокрутке частично или полностью вниз (например, contentOffset.y значительно больше 0).

Понятно, что с iOS 8 самоопределяющимися ячейками оценка высоты строки имеет решающее значение, поэтому Apple действительно необходимо решить эту проблему как можно скорее.

Я подал этот вопрос еще 21 октября 2013 года в качестве радара # 15283329. Пожалуйста, делайте дубликаты отчетов об ошибках, чтобы Apple приостанавливала исправление.

Вы можете прикрепить этот простой образец проекта, чтобы продемонстрировать эту проблему. Он основан непосредственно на собственном образце кода Apple.

Ответ 2

Это была очень неприятная ошибка, но я думаю, что нашел постоянное решение, хотя я не могу в полной мере объяснить, почему.

Вызвать функцию после небольшой (незаметной) задержки:

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

Скажите мне, работает ли это для вас.

Ответ 3

Это определенно ошибка от 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)
        })
    }

Ответ 4

Я нашел временное обходное решение, которое может быть полезно, пока 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;
}

Это не идеально, но это помогло мне. Теперь я могу позвонить:

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

В viewDidLayoutSubviews и он найдет правильное место внизу (или очень близкое оценочное положение).

Я надеюсь, что это поможет.

Ответ 5

В моем случае я нашел временное обходное решение, не предлагая предполагаемую высоту ячейки для программы. Я сделал это, комментируя следующий метод в моем коде:

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

Однако, пожалуйста, обратите внимание, что это может повлиять на работу пользователя при прокрутке пользователя, если ваши ячейки сильно отличаются друг от друга. Для моего случая пока нет заметной разницы.

Надеюсь, что это поможет!

Ответ 6

Мое решение состояло в том, чтобы использовать размер раскадровки в качестве оценки.

Итак, вместо этого:

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

}

Я сделал что-то вроде этого:

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

Я пишу табличное представление чата, поэтому вполне вероятно, что многие из моих ячеек, особенно этот тип текста, будут больше, чем в IB, особенно если сообщение чата очень длинное. Это кажется довольно хорошим... ну... оценка и прокрутка на дно довольно близки. Кажется, это немного хуже, поскольку прокрутка увеличивается дольше, но этого следует ожидать. Я полагаю,

Ответ 7

Просто вызовите tableview reloadData после просмотраDidAppear может решить проблему

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

Ответ 8

В окне раскадровки щелкните в пустой области, чтобы отменить выбор всех видов, затем щелкните представление, в котором есть представление таблицы, а затем нажмите значок Resolve Auto Layout Issue и выберите Reset to Suggested Constraints

enter image description hereenter image description here

Ответ 9

Хотя как smileyborg answer, это ошибка в iOS 8.x, она должна быть исправлена ​​на всех платформах, которые вы поддерживаете...

Чтобы обходной путь на pre-iOS9, ниже код делает трюк без каких-либо dispatch_async или dispatch_after. Протестировано на симуляторе iOS 8.4.

UPDATE: Вызов (только) layoutIfNeeded не работает, когда контроллер просмотра становится видимым при прокрутке UIPageViewController. Поэтому используйте 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);
}

Ответ 10

У меня возникла такая же проблема при создании чата 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)

Пожалуйста, дайте мне знать, если это сработало и для вас.

Ответ 11

Используйте этот простой код для прокрутки вниз

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