如果没有剩余行,PrefetchRowsAt 将不起作用

Posted

技术标签:

【中文标题】如果没有剩余行,PrefetchRowsAt 将不起作用【英文标题】:PrefetchRowsAt won't work if no rows left 【发布时间】:2017-03-07 10:40:04 【问题描述】:

我正在使用UITableView的新prefetchDataSource(也可以应用于UICollectionView。我遇到的问题如下:

    我正在实施func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]),它工作得很好。

    我使用的是array,它以 15 个元素开头。所以numberOfRowsInSection 最初会返回array.count,即 15。

    我想要的是 prefetchRowsAt 函数告诉我我们何时用完了行,以便我从自己的源获取新数据,将这些数据附加到 array 然后reloadData(),这反过来会增加numberOfRowsInSection

    问题是prefetchRowsAt 不会超出第15 行,因为它受到我指定的numberOfRowsInSection 的限制。所以基本上,我被卡住了。基本上,当我在第 5 行时,我希望它告诉我开始预取第 16 行。

我尝试并工作的一个解决方案是将numberOfRowsInSection 的计数增加到100,然后在我获取新数据时放置占位符单元格。但是滚动条看起来太小了,因为它预计有 100 个项目,所以 UX 不会那么好。

也许我在第 3 步中使用的模式是错误的(附加到数组并重新加载数据)?或者您能想出另一种更清洁的解决方案吗?

提前致谢!

【问题讨论】:

我不确定您的问题出在哪里。当prefetchRowsAt: 函数中的 indexPath.row 接近特定偏移量时,您应该开始新的提取。就像indexPath.row+10 >= array.count 一样,您应该开始获取。 @Lefteris 是的,这就是我现在使用'cellForRowAt:'所做的。我想使用“prefetchRowsAt:”来避免这样做,如果可能的话,也是为了让省长依赖于单元格的大小,而不是随机常数“10”。 对于单元格的大小,您可以使用scrollViewDidScroll: 委托方法,看看您距离可滚动内容的末尾还有多远,并据此开始预取。 UITableViewDataSourcePrefetching 协议用于下载已添加到 TableView 数据源的单元格的数据(如预缓存图像),而不是用于分页....See the documentation here @Guy 数组中的数据来自哪里? @Zonker.in.Geneva 网络调用为主,有时会缓存在设备中。 【参考方案1】:

我想我已经理解了你的意思:你的类有一个 mutableArray 来存储你要显示的 Cell 的数据模型。每次返回的资源请求数为15。

第一次:获取的数据数为 15,当用户滚动到第 5 个 indexpath 单元格时,您希望预先从服务器请求下一个资源。 得到请求后,你 mutableArray 的计数是 30

第二次:当用户滚动到第 25 个 indexpath 单元格时,您希望从服务器预先获取下一个资源。 得到请求后,你的 mutableArray 的计数是 45

只要用户滑动屏幕,就永远请求下次资源

好的,不需要使用prefetchDataSource(只有iOS 10可用),而是可以使用dataSource。

当然,如果你只在 ios 10 上运行你的应用程序,你可以通过实现 prefetch 方法在 Cell 中对元数据(如预缓存图像)进行网络请求 .如果没有,您可以使用 cellForRowAtIndexPath(tableView) 或 cellForItemAtIndexPath(collectionView)

对于 UITableView

tableView:cellForRowAtIndexPath:

对于 UICollectionView

collectionView:cellForItemAtIndexPath:

另外,我不建议你使用 scrollViewDidScroll

因为单元格的高度通常会随着数据的变化而变化 ,如果它会更贵。

您的代码可以像下面这样的代码:(UICollectionView)

@interface YourCollectionView()
@property (nonatomic, strong) SDWebImagePrefetcher *imagePrefetcher;
@end

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
   // Check current indexPath

    NSInteger distance = self.array.count - indexPath.row-1;

    if (distance <= 10) 
        [self loadingMoreData];
    

    // Preload the ten image into the cache, if you need your app to be compatible with iOS10 below. 
        NSMutableArray <NSURL*> *URLsArray = [[NSMutableArray alloc] init];

   for (NSInteger i = 1; i <= 10; i++) 
        NSInteger y = currentLastIndexRow + i;
        if (y > self.array.count-1) 
            break;
        
        YourModel *model = self.array[indexPath.item];
        ImageModel *image = model.image;
        [URLsArray addObject:[NSURL URLWithString:image.httpsURL]];
    
    [self.imagePrefetcher prefetchURLs:URLsArray];


    YourCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];

    // setting your Cell

    [cell setNeedsLayout];
    return cell;

如果您的应用不需要兼容iOS10以下,可以将prelaod代码放到prefetch方法中。

顺便说一句:如果您认为我的回答有效,请不要犹豫采纳它:)


#Updates:关于如何重新加载:您可以使用KVO监控阵列, 喜欢:

[self addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

并实现这个方法

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    
    if([keyPath isEqualToString:@"array"])
    
        if(!change) return;

        id oldC = [change objectForKey:NSKeyValueChangeOldKey];
        id newC = [change objectForKey:NSKeyValueChangeNewKey];
        UICollectionView *cv = selfWeak.collectionView;

        // changes is non-nil, so we just need to update 
        [cv performBatchUpdates:^
        // delete reload insert..

         completion:^(BOOL finished) 
            // do somethind , like scrollToItemAtIndexPath
        ];

    

【讨论】:

【参考方案2】:

我认为您应该开始异步加载新数据并插入新行:func insert​Rows(at:​with:​) 而不是 reloadData(您是在插入数据,而不是重新加载它)。

您可以在预取方法到达最后一行(或之前)时立即开始加载新数据。加载后,您可以调用begin​Updates(),更新您的数组,插入新行,最后是end​Updates()

在文档中查看更多信息:Batch Insertion, Deletion, and Reloading of Rows and Sections

【讨论】:

【参考方案3】:

嗯,PrefetchDelegate 的工作方式有点不同。

你需要告诉 tableview 你的系统总共有多少行。

所以基本上你需要传递将在 cellForRowAtIndexPath 中显示的对象表视图的总数。

完成此操作后,prefetchRowsAt 将开始调用,并让您知道它将显示哪一行,您应该在那里应用您的 API 调用逻辑。

如果您需要更多说明,请告诉我。

【讨论】:

以上是关于如果没有剩余行,PrefetchRowsAt 将不起作用的主要内容,如果未能解决你的问题,请参考以下文章

DynamoDB GSI - 仅包含一些行

如果我只使用没有 hostPort 的 containerPort,AWS eb 的 docker 设置将不起作用。(多容器 Docker)

如果在其他类中实现,自定义点击识别器方法将不起作用

Matlab 将[ ]变成NaN

如果我嵌入标签,onEnded 事件将不起作用

查询以将不存在的行包含为 0 值