Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PrefetchRowsAt won't work if no rows left

I am using the new prefetchDataSource of UITableView (also can be applied to UICollectionView. The problem that I am having is the following:

  1. I am implementing func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) which works pretty fine.

  2. I am using an array which starts with 15 elements. So in numberOfRowsInSection will initially return array.count which is 15.

  3. What I want is for the prefetchRowsAt function to tell me when we're runing out of the rows, so that I fetch new data from my own source, append these data to the array and then reloadData(), which will in turn increase the numberOfRowsInSection.

  4. Problem is that prefetchRowsAt won't go beyond row 15 because it is limited by the numberOfRowsInSection that I specified. So basically, I get stuck. Basically, when I am at row 5, I want it to tell me to start prefetching row 16.

One solution that I tried and worked would be to increase the count of numberOfRowsInSection to say 100, and then put placeholder cells while I am fetching the new data. But the scrolling bar will look too small as it is expecting that there is 100 items, so the UX won't be that nice.

Maybe the pattern that I use in step 3 is wrong (append to array and reloadData)? Or Can you think of another cleaner solution to the problem?

Thanks in advance!

like image 914
Guy Daher Avatar asked Mar 07 '17 10:03

Guy Daher


1 Answers

I think I has understood your meaning: Your class have an mutableArray to store your Cell's data model to shown. Everytime, the count of resource request back is 15.

  • at first time: the count of data got is 15, you want to pre-making the request of next resources from server when user scroll to 5th indexpath cell . After get reqeust, your mutableArray's count is 30

  • at second time: you want to pre-get next resources from server when user scroll to 25th indexpath cell . After get reqeust, your mutableArray's count is 45

  • forever making request for next time resouces ,as long as user slides the screen

OK, It's not necessary that using prefetchDataSource(it is only usable iOS 10), rather than you can using dataSource. .

Of course,if you only run your app in iOS 10 , you can making net-request for meta data(like pre-caching images) in Cell by implement prefetch method . If not, you can use cellForRowAtIndexPath(tableView) or cellForItemAtIndexPath(collectionView)

  • For UITableView

    tableView:cellForRowAtIndexPath:

  • For UICollectionView

    collectionView:cellForItemAtIndexPath:

Futher, I'm not suggest you using scrollViewDidScroll,

Becuase the height of the cell usually varies with the change of data ,if it will be more expensive.

your code can like this code below:(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;
}

If you do not need your app to be compatible with iOS10 below,you can put prelaod code to prefetch method.

BTW: If you think my answer working, please do not hesitate to adopt it : )


#Updates: About how to reload: You can monitor array by using KVO, like:

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

and implement this method

-(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
        }];

    }
}
like image 51
0xxxD Avatar answered Oct 21 '22 05:10

0xxxD