我有一个tableView
顶部有 2 个选项卡的分页,最新和流行,我无法使用[tableView beginUpdates]
和正确地为模型更改设置动画[tableView endUpdates]
。
有 3 种加载模式tableView
:
LoadingModeRefresh
:当用户使用拉动刷新时。LoadingModeNewPage
:当用户到达末尾时tableView
需要加载新页面LoadingModeNewTab
:当用户更改顶部的选项卡时
我从后端每页收到 30 个项目(用于分页)
另外我在末尾有一个加载单元,tableView
这样当有人到达表格的末尾时,他/她可以看到它正在加载,当新数据到达时,加载单元被推到末尾tableView
,出视线。
我想要的行为如下:
- 当用户拉动刷新时,将获取 30 个新项目并将其设置为 的支持模型
tableView
,并且tableView
应该重新加载从 0 到 29 的项目,并删除其余的,因为用户可能已经分页超过 1 页,因此可能会有刷新前超过 30 个项目,因此我们需要在刷新后删除多余的项目。 - 当用户到达表的末尾时,会获取 30 个新项目并将其附加
tableView
到tableView
. - 当用户切换选项卡时,我首先希望
tableView
清除所有单元格,以便我提到的加载单元格是唯一的单元格。所以我将 的支持模型设置tableView
为一个空数组,并从tableView
. 获取 30 个新项目后,我将这 30 行插入到tableView
.
我有 3 个属性ViewModel
代表对 indexPaths 的更改:和indexPathsToDelete
,它们与. 我的视图控制器观察这些属性,将它们压缩在一起并立即应用更改。但是不知何故,我的获取代码被调用了两次,这导致 indexPaths 被计算和观察不止一次,这导致与 不一致并导致崩溃。任何人都可以帮助解决问题所在,因为我似乎无法理解发生了什么?indexPathsToInsert
indexPathsToReload
-[RACSignal combinePrevious:reduce:]
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];
}
}];
除此之外,当段选择发生变化时,我只在 tableView 中的最后一个单元格调用[self.viewModel requestNewPage]
时调用。在 viewDidLoad 和刷新处理程序中。tableView:willDisplayCell:
[self.viewModel selectTabWithIndexIfNotSelected:index]
[self.viewModel selectTabWithIndex:0]
[self.viewModel refreshCurrentTab]
我在哪里犯错?