Как определить конец загрузки UITableView - PullRequest
131 голосов
/ 12 ноября 2010

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

В любом случае на SDK знать, когда загрузка uitableview закончилась? Я не вижу ничего ни в делегате, ни в протоколах источника данных.

Я не могу использовать количество источников данных из-за загрузки только видимых ячеек.

Ответы [ 21 ]

1 голос
/ 05 октября 2016

Вот как вы это делаете в Swift 3:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    if indexPath.row == 0 {
        // perform your logic here, for the first row in the table
    }

    // ....
}
0 голосов
/ 20 января 2016

Я знаю, что на это ответили, я просто добавляю рекомендацию.

Согласно следующей документации

https://www.objc.io/issues/2-concurrency/thread-safe-class-design/

Исправление проблем с синхронизацией с dispatch_async - это плохоидея.Я предлагаю, чтобы мы справились с этим, добавив FLAG или что-то в этом роде.

0 голосов
/ 10 марта 2014

В iOS7.0x решение немного другое.Вот что я придумал.

    - (void)tableView:(UITableView *)tableView 
      willDisplayCell:(UITableViewCell *)cell 
    forRowAtIndexPath:(NSIndexPath *)indexPath
{
    BOOL isFinishedLoadingTableView = [self isFinishedLoadingTableView:tableView  
                                                             indexPath:indexPath];
    if (isFinishedLoadingTableView) {
        NSLog(@"end loading");
    }
}

- (BOOL)isFinishedLoadingTableView:(UITableView *)tableView 
                         indexPath:(NSIndexPath *)indexPath
{
    // The reason we cannot just look for the last row is because 
    // in iOS7.0x the last row is updated before
    // looping through all the visible rows in ascending order 
    // including the last row again. Strange but true.
    NSArray * visibleRows = [tableView indexPathsForVisibleRows];   // did verify sorted ascending via logging
    NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject];
    // For tableviews with multiple sections this will be more complicated.
    BOOL isPreviousCallForPreviousCell = 
             self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row;
    BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath];
    BOOL isFinishedLoadingTableView = isLastCell && isPreviousCallForPreviousCell;
    self.previousDisplayedIndexPath = indexPath;
    return isFinishedLoadingTableView;
}
0 голосов
/ 22 августа 2018

Совершенно случайно я наткнулся на это решение:

tableView.tableFooterView = UIView()
tableViewHeight.constant = tableView.contentSize.height

Вам необходимо установить footerView перед получением contentSize, например. в viewDidLoad. Btw. настройка footeView позволяет удалять «неиспользуемые» разделители

0 голосов
/ 03 июня 2017

Если у вас есть несколько разделов, вот как получить последний ряд в последнем разделе (Swift 3):

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if let visibleRows = tableView.indexPathsForVisibleRows, let lastRow = visibleRows.last?.row, let lastSection = visibleRows.map({$0.section}).last {
        if indexPath.row == lastRow && indexPath.section == lastSection {
            // Finished loading visible rows

        }
    }
}
0 голосов
/ 31 августа 2014

@ folex ответ правильный.

Но он будет терпеть неудачу , если в tableView одновременно отображается более одной секции .

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
   if([indexPath isEqual:((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject])]){
    //end of loading

 }
}
0 голосов
/ 24 июля 2014

Я копирую код Эндрю и расширяю его, чтобы учесть случай, когда у вас есть только 1 строка в таблице. Пока это работает для меня!

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// detect when all visible cells have been loaded and displayed
// NOTE: iOS7 workaround used - see: /3440021/kak-opredelit-konets-zagruzki-uitableview
NSArray *visibleRows = [tableView indexPathsForVisibleRows];
NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject];
BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row;
BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath];
BOOL isFinishedLoadingTableView = isLastCell && ([tableView numberOfRowsInSection:0] == 1 || isPreviousCallForPreviousCell);

self.previousDisplayedIndexPath = indexPath;

if (isFinishedLoadingTableView) {
    [self hideSpinner];
}
}

ПРИМЕЧАНИЕ: я просто использую 1 раздел из кода Эндрю, так что имейте это в виду ..

0 голосов
/ 22 сентября 2015

Объектив C

[self.tableView reloadData];
[self.tableView performBatchUpdates:^{}
                              completion:^(BOOL finished) {
                                  /// table-view finished reload
                              }];

Swift

self.tableView?.reloadData()
self.tableView?.performBatchUpdates({ () -> Void in

                            }, completion: { (Bool finished) -> Void in
                                /// table-view finished reload
                            })
0 голосов
/ 07 октября 2015

В Swift вы можете сделать что-то вроде этого.Следующее условие будет выполняться каждый раз, когда вы достигнете конца tableView

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row+1 == postArray.count {
            println("came to last row")
        }
}
0 голосов
/ 25 января 2019

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

В жизненном цикле приложения есть 4 ключевых момента:

  1. Приложение получает событие (касание, таймер, отправка блока и т. Д.)
  2. приложение обрабатывает событие (оно изменяет ограничение, запускает анимацию, меняет фон и т. д.)
  3. Приложение вычисляет новую иерархию представления
  4. Приложение отображает иерархию представления и отображает ее

2 и 3 раза полностью разделены. Почему? По соображениям производительности мы не хотим выполнять все вычисления момента 3 каждый раз, когда выполняется модификация.

Итак, я думаю, что вы сталкиваетесь с таким случаем:

tableView.reloadData()
tableView.visibleCells.count // wrong count oO

Что здесь не так?

Как и в любом представлении, табличное представление перезагружает содержимое с ленивой загрузкой.На самом деле, если вы наберете reloadData несколько раз, это не создаст проблем с производительностью.Табличное представление только повторно вычисляет свой размер контента на основе его реализации делегата и ждет момента 3, чтобы загрузить его ячейки.Это время называется проходом макета.

Хорошо, как попасть на проход макета?

Во время прохода макета приложение вычисляетвсе кадры иерархии представления.Чтобы принять участие, вы можете переопределить выделенные методы layoutSubviews, updateLayoutConstraints и т. Д. В UIView и эквивалентные методы в подклассе контроллера представления.

Это именно то, что делает табличное представление.Он переопределяет layoutSubviews и в зависимости от вашей реализации делегата добавляет или удаляет ячейки.Он вызывает cellForRow прямо перед добавлением и размещением новой ячейки, willDisplay сразу после.Если вы вызвали reloadData или просто добавили табличное представление в иерархию, табличное представление добавляет столько ячеек, сколько необходимо для заполнения своего кадра в этот ключевой момент.

Хорошо, но сейчасКак узнать, когда табличное представление завершило перезагрузку своего содержимого?

Теперь мы можем перефразировать этот вопрос: как узнать, когда табличное представление завершило размещение своих подпредставлений?

Самый простой способ - войти в макет табличного представления:

class MyTableView: UITableView {
    func layoutSubviews() {
        super.layoutSubviews()
        // the displayed cells are loaded
    }
}

Обратите внимание, что этот метод вызывается много раз в жизненном цикле табличного представления.Из-за прокрутки и поведения очереди в табличном представлении ячейки часто изменяются, удаляются и добавляются.Но работает, сразу после super.layoutSubviews(), клетки загружаются.Это решение эквивалентно ожиданию события willDisplay последнего пути индекса.Это событие вызывается во время выполнения layoutSubviews табличного представления для каждой добавленной ячейки.

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

Как описано в документации , вы можете использовать опцию UIView.animate(withDuration:completion):

tableView.reloadData()
UIView.animate(withDuration: 0) {
    // layout done
}

Это решение работает, но экран обновляется один раз между временем макетасделано и время вызова блока.Это эквивалентно решению DispatchMain.async, но указано.

В качестве альтернативы, я бы предпочел принудительно настроить макет представления таблицы

Существует специальный метод длявынудите любое представление немедленно вычислить свои кадры подпредставления layoutIfNeeded:

tableView.reloadData()
table.layoutIfNeeded()
// layout done

Будьте осторожны, однако, это удалит ленивую загрузку, используемую системой.Повторный вызов этих методов может создать проблемы с производительностью.Убедитесь, что они не будут вызваны до того, как будет вычислен кадр табличного представления, иначе табличное представление будет загружено снова, и вы не будете уведомлены.


Я думаю, что не существует идеальногорешение.Подклассы классов могут привести к трюкам.Передача макета начинается сверху и заканчивается снизу, поэтому получить уведомление о завершении макета не так просто.И layoutIfNeeded() может привести к проблемам с производительностью и т. Д. Но, зная эти варианты, вы сможете подумать об одной альтернативе, которая подойдет вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...