Анимация добавления / перезагрузки / удаления строк в разбивке по страницам на UITableView с ReactiveCocoa

У меня есть нумерация страниц tableView с двумя вкладками вверху, "Последние" и "Популярные", и я не могу правильно их анимировать с помощью [tableView beginUpdates] а также [tableView endUpdates],

Есть 3 режима загрузки для tableView:

  • LoadingModeRefresh: когда пользователь обновляется с помощью pull для обновления.
  • LoadingModeNewPage: когда пользователь достигает конца tableView так что новая страница должна быть загружена
  • LoadingModeNewTab: когда пользователь меняет вкладку вверху

Я получаю 30 элементов на страницу из бэкэнда (для нумерации страниц)

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

Мне нужно следующее поведение:

  • Когда пользователь тянет для обновления, 30 новых элементов извлекаются и устанавливаются в качестве вспомогательной модели для tableViewи tableView следует перезагрузить элементы от 0 до 29 и удалить остальные, поскольку пользователь мог разместить на странице более 1 страницы, поэтому до обновления может быть более 30 элементов, поэтому нам необходимо удалить дополнительные элементы после обновления.
  • Когда пользователь достигает конца таблицы, 30 новых элементов извлекаются и добавляются в конец вспомогательной модели для tableViewи мы должны вставить эти 30 строк в tableView,
  • Когда пользователь переключает вкладки, я сначала хочу tableView быть очищенным от всех ячеек так, чтобы ячейка загрузки, о которой я упоминал, была единственной ячейкой. поэтому я установил модель поддержки tableView в пустой массив, и удалите все строки из tableView, после получения 30 новых элементов я вставляю эти 30 строк в tableView,

У меня 3 свойства на моем ViewModel представление изменений в indexPaths: indexPathsToDelete, indexPathsToInsert а также indexPathsToReload, которые связаны с изменениями в модели поддержки с -[RACSignal combinePrevious:reduce:], Мой контроллер представления наблюдает за этими свойствами, объединяет их в архив и применяет изменения сразу. однако каким-то образом мой код выборки вызывается дважды, что приводит к тому, что indexPaths вычисляется и наблюдается более одного раза, что вызывает несоответствия с tableView и вызывает сбои. Может кто-нибудь помочь с тем, где проблема, так как я не могу понять, что происходит?

вот код для ViewModel (извините за код, пока не реорганизовал его, так как у меня его пока нет):

const NSInteger kArticlePerPage = 30;

@interface FeedViewModel ()

@property (nonatomic, readwrite) Source *model;
@property (nonatomic) LoadingMode loadingMode;

@property (nonatomic) NSInteger selectedTabIndex;
@property (nonatomic, readwrite) NSInteger pagesRequested;
@property (nonatomic, readwrite) NSInteger pagesLoaded;

@property (nonatomic, readwrite) NSArray *indexPathsToDelete;
@property (nonatomic, readwrite) NSArray *indexPathsToInsert;
@property (nonatomic, readwrite) NSArray *indexPathsToReload;

- (NSArray *)indexPathsForRange:(NSRange)range inSection:(NSInteger)section;

@end

@implementation FeedViewModel

- (instancetype)initWithModel:(Source *)source
{
    self = [super init];

    if (!self) {
        return nil;
    }

    _model = source;
    _pagesLoaded = 0;

    RACSignal *loadingModeSignal = RACObserve(self, loadingMode);
    RACSignal *newTabSignal = [loadingModeSignal  //
        filter:^BOOL(NSNumber *loadingMode) {
            return loadingMode.integerValue == LoadingModeNewTab;
        }];
    RACSignal *newPageSignal = [loadingModeSignal  //
        filter:^BOOL(NSNumber *loadingMode) {
            return loadingMode.integerValue == LoadingModeNewPage;
        }];
    RACSignal *refreshSignal = [loadingModeSignal  //
        filter:^BOOL(NSNumber *loadingMode) {
            return loadingMode.integerValue == LoadingModeRefresh;
        }];

    RAC(self, loading) = [loadingModeSignal  //
        map:^id(NSNumber *loadingMode) {
            switch (loadingMode.integerValue) {
                case LoadingModeFinished:
                    return @(NO);

                default:
                    return @(YES);
            }
        }];

    @weakify(self);

    RACSignal *newArticlesSignal = [[[[RACSignal
        combineLatest:@[ RACObserve(self, pagesRequested), RACObserve(self, selectedTabIndex) ]]
        sample:RACObserve(self, loadingMode)]  //
        map:^id(RACTuple *tuple) {
            @strongify(self);
            return [self signalForNewArticlesForPage:tuple.first order:[tuple.second integerValue]];
        }]  //
        switchToLatest];

    RACSignal *articlesForNewTabSignal = [[newTabSignal  //
        flattenMap:^RACStream * (id value) {             //
            return [newArticlesSignal startWith:@[]];
        }]  //
        skip:1];

    RACSignal *articlesForNewPageSignal = [newPageSignal  //
        flattenMap:^RACStream * (id value) {
            return [newArticlesSignal  //
                map:^id(NSArray *newArticles) {
                    @strongify(self);

                    Article *article = self.articles[0];
                    NSLog(@"article name: %@", article.title);
                    return [self.articles arrayByAddingObjectsFromArray:newArticles];

                }];
        }];
    RACSignal *articlesForRefreshSignal = [refreshSignal  //
        flattenMap:^RACStream * (id value) {              //
            return newArticlesSignal;
        }];

    RAC(self, articles) = [RACSignal merge:@[
        articlesForNewTabSignal,   //
        articlesForNewPageSignal,  //
        articlesForRefreshSignal
    ]];


    RACSignal *articlesSignal = RACObserve(self, articles);

    RAC(self, indexPathsToDelete) = [articlesSignal  //
        combinePreviousWithStart:@[]                 //
                          reduce:^id(NSArray *previous, NSArray *current) {
                              @strongify(self);
                              if (previous.count > current.count) {
                                  return [self
                                      indexPathsForRange:NSMakeRange(current.count,
                                                                     previous.count - current.count)
                                               inSection:0];
                              }
                              else {
                                  return @[];
                              }
                          }];

    RAC(self, indexPathsToInsert) = [articlesSignal                          //
        combinePreviousWithStart:@[]                                         //
                          reduce:^id(NSArray *previous, NSArray *current) {  //
                              @strongify(self);
                              if (previous.count < current.count) {
                                  return [self
                                      indexPathsForRange:NSMakeRange(previous.count,
                                                                     current.count - previous.count)
                                               inSection:0];
                              }
                              else {
                                  return @[];
                              }
                          }];

    RAC(self, indexPathsToReload) = [articlesSignal  //
        combinePreviousWithStart:@[]                 //
                          reduce:^id(NSArray *previous, NSArray *current) {
                              if (previous.count >= current.count) {
                                  return [self indexPathsForRange:NSMakeRange(0, current.count)
                                                        inSection:0];
                              }
                              else {
                                  return @[];
                              }
                          }];

    RAC(self, pagesLoaded) = [[RACObserve(self, articles)  //
        skip:1]                                            //
        map:^id(NSArray *array) {                          //
            NSInteger pages = array.count / kArticlePerPage;
            if (array.count % kArticlePerPage != 0) {
                pages++;
            }
            return @(pages);
        }];


    RAC(self, separatorColorHexString) = [RACObserve(self, model.type) map:^id(NSNumber *type) {
        if (type.integerValue == SourceTypeInspiration) {
            return @"ffffff";
        }
        else {
            return @"E5E5E5";
        }
    }];

    RAC(self, segmentTitles) = [RACObserve(self, model)  //
        map:^id(Source *source) {

            NSMutableArray *titles = [NSMutableArray array];

            if (source.isPopularAvailable) {
                [titles addObject:@"Popular"];
            }

            if (source.isLatestAvailable) {
                [titles addObject:@"Latest"];
            }

            return titles;
        }];
    return self;
}

- (void)setCurrentSource:(Source *)source
{
    self.model = source;
}

- (void)refreshCurrentTab
{
    self.pagesRequested = 1;
    self.loadingMode = LoadingModeRefresh;
}

- (void)requestNewPage
{
    if (self.pagesRequested == self.pagesLoaded + 1) {
        return;
    }

    self.pagesRequested = self.pagesLoaded + 1;
    self.loadingMode = LoadingModeNewPage;
}

- (void)selectTabWithIndex:(NSInteger)index
{
    self.selectedTabIndex = index;
    self.pagesRequested = 1;
    self.loadingMode = LoadingModeNewTab;
}

- (void)selectTabWithIndexIfNotSelected:(NSInteger)index
{
    if (self.selectedTabIndex == index) {
        return;
    }

    [self selectTabWithIndex:index];
}

- (NSArray *)indexPathsForRange:(NSRange)range inSection:(NSInteger)section
{
    NSMutableArray *indexes = [NSMutableArray array];
    for (NSUInteger i = range.location; i < range.location + range.length; i++) {
        [indexes addObject:[NSIndexPath indexPathForRow:i inSection:section]];
    }

    return [indexes copy];
}

- (RACSignal *)signalForNewArticlesForPage:(NSNumber *)pageNumber order:(ArticleOrder)order
{
    return [[SourceManager sharedManager] articlesForSourceKey:self.model.key
                                                  articleOrder:order
                                                    pageNumber:pageNumber];
}

- (void)setLoadingMode:(LoadingMode)loadingMode
{
    _loadingMode = loadingMode;
}

@end

и код для наблюдения массивов indexPath в ViewController.m:

RACSignal *toDeleteSignal = [RACObserve(self, viewModel.indexPathsToDelete)  //
    deliverOn:[RACScheduler mainThreadScheduler]];                           //

RACSignal *toInsertSignal = [RACObserve(self, viewModel.indexPathsToInsert)  //
    deliverOn:[RACScheduler mainThreadScheduler]];                           //

RACSignal *toReloadSignal = [RACObserve(self, viewModel.indexPathsToReload)  //
    deliverOn:[RACScheduler mainThreadScheduler]];

[[RACSignal zip:@[ toDeleteSignal, toInsertSignal, toReloadSignal ]]
    subscribeNext:^(RACTuple *tuple) {

        @strongify(self);

        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:tuple.first
                              withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.tableView insertRowsAtIndexPaths:tuple.second
                              withRowAnimation:UITableViewRowAnimationTop];
        [self.tableView reloadRowsAtIndexPaths:tuple.third
                              withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.tableView endUpdates];

        if (self.tableView.pullToRefresh.state == BPRPullToRefreshStateLoading) {
            [self.tableView.pullToRefresh dismiss];
        }

    }];

Кроме этого я звоню только [self.viewModel requestNewPage] когда tableView:willDisplayCell: вызывается для последней ячейки в tableView, [self.viewModel selectTabWithIndexIfNotSelected:index] когда выбор сегмента меняется. [self.viewModel selectTabWithIndex:0] в viewDidLoad и [self.viewModel refreshCurrentTab] в обработчике обновления.

Где я делаю ошибку?

0 ответов

Другие вопросы по тегам