Получить уведомление, когда UITableView закончит запрашивать данные?

108

Есть ли способ узнать, когда a UITableViewзакончил запрашивать данные из своего источника данных?

Ни один из методов viewDidLoad/ viewWillAppear/ viewDidAppearсвязанного контроллера представления ( UITableViewController) здесь не используется, поскольку все они срабатывают слишком рано. Ни один из них (вполне понятно) не гарантирует, что запросы к источнику данных на данный момент завершены (например, пока представление не будет прокручено).

Один из способов я нашел позвонить reloadDataв viewDidAppear, так как , когда reloadDataвозвращается, как таблица будет гарантирован завершением запроса источника данных столько , сколько ему нужно , чтобы до поры до времени.

Однако это кажется довольно неприятным, поскольку я предполагаю, что это приводит к тому, что источник данных запрашивает одну и ту же информацию дважды (один раз автоматически и один раз из-за reloadDataвызова) при первой загрузке.

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

При восстановлении позиции прокрутки (с помощью scrollRectToVisible:animated:) мне нужно, чтобы в табличном представлении уже было достаточно данных, иначе scrollRectToVisible:animated:вызов метода ничего не делает (что происходит, если вы размещаете вызов самостоятельно в любом из viewDidLoad, viewWillAppearили viewDidAppear).

kennethmac2000
источник
Кажется, что вы ищете что-то похожее на этот stackoverflow.com/a/11672379/2082172 .
Тимур Кучкаров
По моему опыту, UITableView может кэшировать вызовы reloadData, точно так же, как кэширует вызовы для вставки / удаления строк и других вещей. UITableView вызовет делегат источника данных, когда он будет готов, в зависимости от использования процессора и других вещей.
Уолт Селлерс,

Ответы:

61

Этот ответ, похоже, больше не работает из-за некоторых изменений, внесенных в реализацию UITableView с момента написания ответа. См. Этот комментарий: Получать уведомление, когда UITableView завершил запрос данных?

Я играл с этой проблемой в течение нескольких дней , и думаю , что подклассы UITableView«s reloadDataэто лучший подход:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDataне заканчивается до того, как таблица завершит перезагрузку данных. Итак, когда NSLogзапускается второй , табличное представление фактически завершило запрос данных.

Я создал подклассы UITableViewдля отправки методов делегату до и после reloadData. Работает как часы.

Эрик МОРАНД
источник
2
Должен сказать, это кажется лучшим решением. Просто реализовал это и, как вы говорите, работает как шарм. Спасибо!
theory
2
@EricMORAND Вы говорите, что «reloadData не заканчивается, пока таблица не завершит перезагрузку своих данных». Вы можете пояснить, что вы имеете в виду? Я обнаружил, что это происходит reloadDataнемедленно, и я вижу «END reloadData» до того, как ячейки будут фактически перезагружены (т.е. до вызова UITableViewDataSourceметодов). Мои эксперименты демонстрируют прямо противоположное тому, что вы говорите. Я должен неправильно понять, что вы пытаетесь сказать.
Роб
3
Разве ответ Эрика не такой же, как отказ от реализации (чтение без переопределения) reloaddata, вызов reloaddata (который по сути будет [super reloaddata], а затем, после его вызова, делать то, что вы хотите, по его завершении?
Нирав Бхатт
5
Давным-давно reloadData завершила процесс загрузки до его завершения. Но в какой-то момент Apple изменила это. Теперь класс UITableView может кэшировать вызов reloadData со всеми вызовами вставки и удаления строк. Если вы посмотрите на объявление @interface для UITableView, вы найдете член NSMutableArray _reloadItems прямо под _insertItems и _deleteItems. (Из-за этого изменения мне пришлось переделать код, который я унаследовал.)
Уолт Селлерс,
3
Проводка кода завершения в блоке на главной очереди после вызова [super reloadData]работает для меня: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Это в основном перепрыгивает через блоки, которые публикуются в табличном представлении reloadData.
Timothy Moose
53

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

Наконец, это единственное, что у меня получилось.

[yourTableview reloadData];

dispatch_async(dispatch_get_main_queue(),^{
        NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
        //Basically maintain your logic to get the indexpath
        [yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
 });

Быстрое обновление:

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
    //Basically maintain your logic to get the indexpath
    yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)

})

Итак, как это работает.

В основном, когда вы выполняете перезагрузку, основной поток становится занятым, поэтому в то время, когда мы выполняем асинхронный поток отправки, блок будет ждать завершения основного потока. Итак, как только tableview будет полностью загружен, основной поток будет завершен, и поэтому он отправит наш блок метода

Протестировано на iOS7 и iOS8, отлично работает;)

Обновление для iOS9: отлично работает и iOS9. Я создал образец проекта в github как POC. https://github.com/ipraba/TableReloadingNotifier

Прилагаю сюда скриншот своего теста.

Протестированная среда: симулятор iOS9 iPhone6 ​​от Xcode7

введите описание изображения здесь

ипраба
источник
8
лучший ответ, который я нашел!
Николай Шубенков
@Gon, я сделал тест на iOS9, и он отлично работает. Не могли бы вы сослаться на github.com/ipraba/TableReloadingNotifier
ipraba
Он отлично работает на моем эмуляторе, но, похоже, не работает на реальном устройстве. Кто-нибудь еще с этим вопросом?
sosale151
1
Это решение наконец-то работает для меня! он отлично работает на iOS 9
Strong
1
Я тестировал на реальном устройстве iPhone 6s. Он тоже отлично работает.
Strong
25

РЕДАКТИРОВАТЬ: этот ответ на самом деле не является решением. Вероятно, сначала это работает, потому что перезагрузка может происходить довольно быстро, но на самом деле блок завершения не обязательно вызывается после того, как данные полностью завершили перезагрузку, потому что reloadData не блокируется. Возможно, вам стоит поискать лучшее решение.

Чтобы расширить ответ @Eric MORAND, давайте добавим блок завершения. Кому не нравится блок?

@interface DUTableView : UITableView

   - (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;

@end

и...

#import "DUTableView.h"

@implementation DUTableView

- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [super reloadData];
    if(completionBlock) {
        completionBlock();
    }
}

@end

Использование:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];
Bandejapaisa
источник
2
Мне не хватает блоков. Кому нужен делегат, когда у тебя хороший блок!
bandejapaisa
Мне это нравится, но как насчет случаев, когда система вызывает reloadData (), например, когда таблица отображается впервые?
Symmetric
12
Это не решение. Блок завершения запускает выполнение до cellForRowAtIndexPath
zvjerka24
1
Это не будет работать; reloadData не является методом блокировки. Этот код вызывается сразу после вызова reloadData, даже если ячейки еще не перезагружены. Кроме того, посмотрите на этот код, и вы увидите, что вы можете просто поместить свой код после reloadData .
colinta
1
У меня это работает с небольшими изменениями. Вместо того , чтобы вызвать блок завершения напрямую, вызовите его в блоке , размещенном на главной очереди: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Это в основном перепрыгивает блоки, размещенные в табличном представлении reloadData.
Timothy Moose
11

reloadData просто запрашивает данные для видимых ячеек. Говорит, чтобы получить уведомление, когда указанная часть вашей таблицы загружена, подключите tableView: willDisplayCell:метод.

- (void) reloadDisplayData
{
    isLoading =  YES;
    NSLog(@"Reload display with last index %d", lastIndex);
    [_tableView reloadData];
    if(lastIndex <= 0){
    isLoading = YES;
    //Notify completed
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row >= lastIndex){
    isLoading = NO;
    //Notify completed
}
Черный кодер
источник
1
Я не уверен, что это сработает. Последний индекс - это конец данных таблицы (например, 100 записей), но таблица будет отображать только то, что видно на экране (например, 8 записей).
Travis M.
10

Это мое решение. 100% работает и используется во многих проектах. Это простой подкласс UITableView.

@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end

@interface MyTableView : UITableView {
    struct {
        unsigned int delegateWillReloadData:1;
        unsigned int delegateDidReloadData:1;
        unsigned int reloading:1;
    } _flags;
}
@end

@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
    return (id<MyTableViewDelegate>)[super delegate];
}

- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
    [super setDelegate:delegate];
    _flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
    _flags.delegateDidReloadData = [delegate    respondsToSelector:@selector(tableViewDidReloadData:)];
}

- (void)reloadData {
    [super reloadData];
    if (_flags.reloading == NO) {
        _flags.reloading = YES;
        if (_flags.delegateWillReloadData) {
            [(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
        }
        [self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
    }
}

- (void)finishReload {
    _flags.reloading = NO;
    if (_flags.delegateDidReloadData) {
        [(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
    }
}

@end

Это похоже на решение Джоша Брауна, за одним исключением. В методе performSelector задержка не требуется. Независимо от того, сколько времени reloadDataзаймет. tableViewDidLoadData:всегда срабатывает, когда tableViewзаканчивает спрашивать dataSource cellForRowAtIndexPath.

Даже если вы не хотите создавать подклассы, UITableViewвы можете просто вызвать, [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]и ваш селектор будет вызван сразу после завершения перезагрузки таблицы. Но вы должны убедиться, что селектор вызывается только один раз за вызов reloadData:

[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];

Наслаждаться. :)

Марк Крыжановский
источник
1
Использование функции performSelector просто великолепно. Простой и рабочий, спасибо
Юлия
Это просто потрясающе. Я спорил с этим несколько дней. Спасибо!
Дэвид Каррико
@MarkKryzhanouski, а на iOS 9 тестировали?
Виктор
@MarkKryzhanouski, спасибо за оперативный отзыв! Отлично работает на iOS 7 и 8.
Виктор
Решения performSelectorили выполнение в основном потоке dispatch_asynchне работают на iOS 9 .
Мануэль
8

Это ответ на несколько другой вопрос: мне нужно было знать, когда я UITableViewтоже закончил звонить cellForRowAtIndexPath(). Я layoutSubviews()выделил подклассы (спасибо @Eric MORAND) и добавил обратный вызов делегата:

SDTableView.h:

@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end

@interface SDTableView : UITableView

@property(nonatomic,assign) id <SDTableViewDelegate> delegate;

@end;

SDTableView.m:

#import "SDTableView.h"

@implementation SDTableView

@dynamic delegate;

- (void) reloadData {
    [self.delegate willReloadData];

    [super reloadData];

    [self.delegate didReloadData];
}

- (void) layoutSubviews {
    [self.delegate willLayoutSubviews];

    [super layoutSubviews];

    [self.delegate didLayoutSubviews];
}

@end

Использование:

MyTableViewController.h:

#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;

MyTableViewController.m:

#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if ( ! reloadInProgress) {
        NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
        reloadInProgress = TRUE;
    }

    return 1;
}

- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}

- (void)didLayoutSubviews {
    if (reloadInProgress) {
        NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
        reloadInProgress = FALSE;
    }
}

ПРИМЕЧАНИЯ: Поскольку это подкласс, у UITableViewкоторого уже есть свойство делегата, указывающее на MyTableViewControllerто, нет необходимости добавлять еще один. «@Dynamic delegate» указывает компилятору использовать это свойство. (Вот ссылка с описанием этого: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

UITableViewСвойство в MyTableViewControllerдолжно быть изменено , чтобы использовать новый SDTableViewкласс. Это делается в Инспекторе удостоверений Interface Builder. Выделите UITableViewвнутреннюю часть UITableViewControllerи установите для «Custom Class» значение SDTableView.

симметричный
источник
Где вы устанавливаете MyTableViewController в качестве делегата для SDTableView? Каким образом возможно установить свойство с именем «делегат» в SDTableView, если его суперкласс уже имеет свойство с таким именем (UITableView.delegate)? Как прикрепить пользовательский SDTableView к свойству MyTableViewController.tableView, если свойство имеет тип «UITablewView», а объект (экземпляр SDTableView) имеет тип «SDTableView»? Я борюсь с той же проблемой, поэтому я надеюсь, что есть решение этих проблем :)
Эрл Грей
В коде Symmetric (SDTableView) не следует устанавливать reloadInProgress в значение FALSE в didReloadData вместо didLayoutSubviews? Поскольку reloadData вызывается после layoutSubviews, и загрузку не следует считать выполненной до завершения reloadData.
tzuchien.chiu
Это не работает для iOS 8, пришлось включить ответ iPrabu .
Stunner
6

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

Попробуй это:

В viewDidLoadписьменной форме,

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];

и добавьте этот метод в свой viewController:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"]) {
        DLog(@"change = %@", change.description)

        NSValue *new = [change valueForKey:@"new"];
        NSValue *old = [change valueForKey:@"old"];

        if (new && old) {
            if (![old isEqualToValue:new]) {
                // do your stuff
            }
        }


    }
}

Вам могут потребоваться небольшие изменения в проверке изменений. Хотя это сработало для меня.

Ура! :)

Абдулла Умер
источник
1
Очень умный. У меня отлично работает. Спасибо, что поделился!
DZenBot 02
2
Элегантно! Единственное, что я хотел бы добавить, это то, что вам нужно удалить себя и наблюдателя (возможно, в методе -dealloc). Или добавьте себя в качестве наблюдателя -viewWillAppearи удалите себя из -viewWillDisapearметода.
Loozie
2

Вот возможное решение, хотя это и есть взлом:

[self.tableView reloadData];
[self performSelector:@selector(scrollTableView) withObject:nil afterDelay:0.3];

Где ваш -scrollTableViewметод прокручивает представление таблицы с помощью -scrollRectToVisible:animated:. И, конечно же, вы можете настроить задержку в приведенном выше коде от 0,3 до того, что вам подходит. Да, это смехотворно взломано, но у меня работает на моих iPhone 5 и 4S ...

Джош Браун
источник
2
Это может показаться «хакерским», но это действительно стандартный подход к прокрутке: отложите его до следующего цикла выполнения. Я бы изменил задержку на 0, так как это работает так же хорошо и имеет меньшую задержку.
phatmann
У меня не получилось с задержкой 0; 0,3 - это самый низкий показатель, на который я мог пойти и все равно получить данные.
Джош Браун
@phatmann Это сработало для меня. Я также смог использовать 0 для задержки. Спасибо вам обоим.
mcphersonjr
1

Думаю, у меня было нечто подобное. Я добавил BOOL в качестве переменной экземпляра, которая сообщает мне, было ли восстановлено смещение, и проверяет это -viewWillAppear:. Когда он не был восстановлен, я восстанавливаю его этим методом и устанавливаю BOOL, чтобы указать, что я действительно восстановил смещение.

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

Joost
источник
Да, проблема в том, что viewWillAppear кажется слишком ранним (по крайней мере, в моем сценарии). Попытка восстановить смещение в viewWillAppear ничего не дает - если я сначала не добавлю хитрость вызова reloadData. Насколько я понимаю, viewWillAppear / viewDidAppear ссылаются только на фактически появляющееся представление таблицы - они не делают никаких заявлений о том, были ли ячейки таблицы в представлении либо перечислены, либо заполнены ... и это должно произойти, прежде чем вы сможете восстановить смещение, иначе вы восстановите смещение для пустого представления (и я понимаю, почему это не сработает!).
kennethmac2000,
Но, возможно, вы имели в виду, что сначала вызываете загрузку ячеек таблицы, вызывая reloadData в viewWillAppear?
kennethmac2000,
Нет, я не заставляю перезагружать. Когда у меня возникла проблема, я попытался восстановить смещение в -viewDidLoad(где это, конечно, должно было произойти), но это сработало только тогда, когда я установил смещение анимированным. Перемещение настройки смещения на -viewWillAppear:это сработало, но мне пришлось поддерживать флаг, чтобы установить его только один раз. Я полагаю, что табличное представление перезагружает свои данные после добавления в представление, так что это уже есть -loadView. Вы уверены, что ваши данные доступны при загрузке просмотра? Или он загружается в отдельном потоке, что ли?
Joost
Хорошо, возможно, вы поможете мне понять здесь. Вот как я понимаю последовательность вызовов при создании UITableView. 1) viewDidLoad запускается первым. Это указывает на то, что UITableView загружен в память. 2) viewWillAppear находится рядом с огнем. Это указывает на то, что UITableView будет отображаться, но не обязательно, что все видимые объекты UITableViewCell будут полностью созданы / завершены.
kennethmac2000,
2
И все вышеперечисленное верно, тогда возникает вопрос: какие нехакерские варианты у нас есть, чтобы узнать, когда UITableView получил достаточно информации из своего источника данных, чтобы гарантировать, что запрос прокрутки (то есть scrollRectToVisible: animated: call ) действительно будет работать (а не просто ничего не делать)?
kennethmac2000,
1

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

Об этом есть несколько статей. Это один.

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

Уолт Селлерс
источник
1

Вы можете попробовать следующую логику:

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    if ( [self chkIfLastCellIndexToCreate:tableView :indexPath]){
        NSLog(@"Created Last Cell. IndexPath = %@", indexPath);
        //[self.activityIndicator hide];
        //Do the task for TableView Loading Finished
    }
    prevIndexPath = indexPath;

    return cell;
}



-(BOOL) chkIfLastCellIndexToCreate:(UITableView*)tableView : (NSIndexPath *)indexPath{

    BOOL bRetVal = NO;
    NSArray *visibleIndices = [tableView indexPathsForVisibleRows];

    if (!visibleIndices || ![visibleIndices count])
        bRetVal = YES;

    NSIndexPath *firstVisibleIP = [visibleIndices objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIndices objectAtIndex:[visibleIndices count]-1];

    if ((indexPath.row > prevIndexPath.row) && (indexPath.section >= prevIndexPath.section)){
        //Ascending - scrolling up
        if ([indexPath isEqual:lastVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    } else if ((indexPath.row < prevIndexPath.row) && (indexPath.section <= prevIndexPath.section)) {
        //Descending - scrolling down
        if ([indexPath isEqual:firstVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    }
    return bRetVal;
}

И прежде чем вызывать reloadData, установите для prevIndexPath значение nil. Подобно:

prevIndexPath = nil;
[mainTableView reloadData];

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

Джо М
источник
0

наконец, я заставил свой код работать с этим -

[tableView scrollToRowAtIndexPath:scrollToIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

было несколько вещей, о которых нужно было позаботиться -

  1. назовите это внутри " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. просто убедитесь, что сообщение scrollToRowAtIndexPath отправлено соответствующему экземпляру UITableView, которым в данном случае определенно является MyTableview.
  3. В моем случае UIView - это представление, которое содержит экземпляр UITableView
  4. Кроме того, это будет вызываться для каждой нагрузки ячейки. Поэтому добавьте логику в cellForRowAtIndexPath, чтобы избежать повторного вызова scrollToRowAtIndexPath.
smile.al.d.way
источник
и просто убедитесь, что эта часть не вызывается более одного раза. в противном случае блокирует прокрутку.
smile.al.d.way 06
Это может сработать, но это определенно не изящно и не то решение, которое я ищу. К сожалению, похоже, нет лучшего способа определить, действительно ли -reloadData завершил получение данных ...
Джош Браун,
0

Вы можете изменить размер представления таблицы или установить размер содержимого в этом методе, когда все данные загружены:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.frame =CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height);
}
Банкир Миттал
источник
0

Я просто запускаю повторяющийся запланированный таймер и делаю его недействительным только тогда, когда contentSize таблицы больше, когда высота tableHeaderView (означает, что в таблице есть содержимое строк). Код на C # (монотач), но, надеюсь, идея понятна:

    public override void ReloadTableData()
    {
        base.ReloadTableData();

        // don't do anything if there is no data
        if (ItemsSource != null && ItemsSource.Length > 0)
        {
            _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.MinValue, 
                new NSAction(() => 
                {
                    // make sure that table has header view and content size is big enought
                    if (TableView.TableHeaderView != null &&
                        TableView.ContentSize.Height > 
                            TableView.TableHeaderView.Frame.Height)
                    {
                        TableView.SetContentOffset(
                            new PointF(0, TableView.TableHeaderView.Frame.Height), false);
                        _timer.Invalidate();
                        _timer = null;
                    }
                }));
        }
    }
Евгений Тютюнник
источник
0

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

Эрик МОРАНД
источник
0

Начиная с iOS 6, UITableviewметод делегата вызывал:

-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

будет выполняться после успешной перезагрузки вашей таблицы. Вы можете выполнить настройку по мере необходимости в этом методе.

Бхарат Ванкиредди
источник
0

Лучшее решение, которое я нашел в Swift

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}
YannSteph
источник
-1

Почему просто не продлить?

@interface UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end

@implementation UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [self reloadData];
    if(completionBlock) {
        completionBlock();
    }
}
@end

прокрутите до конца:

[self.table reloadDataWithCompletion:^{
    NSInteger numberOfRows = [self.table numberOfRowsInSection:0];
    if (numberOfRows > 0)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberOfRows-1 inSection:0];
        [self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
}];

Не тестировалось с большим количеством данных

Андрей Тищенко
источник