0

i was trying to create a collection view with a list of cells. And when user hits a button it will random select one of the cell.

Now suppose there are only 10 cells. i.e numberOfItemsInSection delegate return 10. The view controller is the data source for the collectionView. The collection view is called myCollectionView. And it has a property called selectedIndexPath

So ViewController:

@interface ViewController () < UICollectionViewDataSource>
@property (nonatomic, strong) NSIndexPath * selectedIndexPath;
@property (strong, nonatomic) IBOutlet UICollectionView *myCollectionView;
@end

Here's my random select code in the view controller:

-(void)chooseRandom{
  NSInteger randomShadeIndex = arc4random_uniform((uint32_t)10);
  NSIndexPath *indexPath = [NSIndexPath indexPathForItem:randomShadeIndex inSection:0];
  self.selectedIndexPath = indexPath;
  [self.myCollectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
}

And here's my cellForItemAtIndexPath in my view controller

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
  MyCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
  cell.selected = self.selectedIndexPath == indexPath;
  return cell;
}

And here's setSelected: method in MyCell:

- (void)setSelected:(BOOL)selected{
    [super setSelected:selected];

    if (selected){
        self.backgroundColor = [UIColor redColor];
    } else {
        self.backgroundColor = [UIColor greenColor];
    }
}

So now when i call chooseRandom from the button press. If the random cell to be selected is not visible(not in current screen), then there's a high chance that it doesn't end up having setSelected:YES called during the selectItemAtIndexPath:.(Or it gets called but sets the cell.selected to NO instead of YES). Meaning the resulting screen has none of the cell selected.

And the interesting thing is, when i tried touching the screen (without selecting any cell). It will called the setSelected: on the cell to be selected. So i think selectItemAtIndexPath: is bugged.

And this only happens, when prefetching enabled in interface builder is set to be enabled.(which is the default for ios 10).

And i've tried following ways to solve this, but none of them works:

  1. Add [self.myCollectionView cellForItemAtIndexPath:indexPath].selected = YES; at the end of chooseRandom.

  2. Use scrollToItemAtIndexPath along with method 1 instead of selectItemAtIndexPath:

I think either this is a bug or I ignore something completely. I've been stuck on this for hours and couldn't figure out why. Now i think it is most likely a bug for selectItemAtIndexPath with prefetching enabled is set.

Please help me and tell me if you encounter the same issue. Thanks!

EDIT: not sure if the same question. this link has similar issue but with deselect

Community
  • 1
  • 1
dknyxh
  • 31
  • 5
  • Try calling scrollToItemAtIndexPath: and the next line call selectItemAtIndexPath: – Jayaraj Jan 20 '17 at 04:58
  • @jayarj I tried that and it didn't work. Also i think it was supposed to scroll in selectItemAtIndexPath. – dknyxh Jan 20 '17 at 17:08
  • When prefetching is enabled the collectionView(_:cellForItemAt:) method on the collection view delegate is called in advance of when the cell is required. To avoid inconsistencies in the visual appearance, use the collectionView(_:willDisplay:forItemAt:) delegate method to update the cell to reflect visual state such as selection. – SPatel Jan 12 '18 at 08:24
  • The Note in the [docs](https://developer.apple.com/documentation/uikit/uicollectionview/1771771-prefetchingenabled) says that if you use prefetchingEnabled you have to use `willDisplayCell` to update your cell appearance (such as selection state) – Islam Oct 25 '19 at 22:22

2 Answers2

0

A UICollectionView, just like a UITableView, caches its cells. Meaning, that if you have 100 cells, and only 10 of them are visible, it will only create 10 cells and reuse them as you scroll, setting the model/data of the visible cells as you are scrolling, reusing the ones that are not visible anymore (to save memory). So the reason you are not able to select a cell that is not visible, might be because it hasn't been created.

Drake
  • 2,679
  • 4
  • 45
  • 88
  • Thanks, I understand what are you saying. But i don't think it is the issue here. If i disable prefetching then there's no problem at all. This only happens when i enable the prefetching.(prefetching is enabled by default in ios10) – dknyxh Jan 20 '17 at 06:54
0

Please check this if it helps you

Can download sample project

https://www.dropbox.com/s/oeo9p1h3e8xfpfj/CollectionView.zip?dl=0?

-(void)chooseRandom{
   NSInteger randomShadeIndex = arc4random_uniform((uint32_t)50);
   NSLog(@"%ld",(long)randomShadeIndex);
   NSIndexPath *indexPath = [NSIndexPath indexPathForItem:randomShadeIndex inSection:0];
   self.selectedIndexPath = indexPath;

   [self.myCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:true];
  [_myCollectionView reloadData]; 
}
Jayaraj
  • 342
  • 3
  • 13
  • Thanks for the response. But i don't think your answer solved this issue. And actually, i think yours is actually incorrect since the visible cells property only gives visible cell and what i want is to choose a random cell in all cells. And scrollToItemAtIndexPath already has it animated set to true, the usage of [UIView animateWithDuration] is not necessary. – dknyxh Jan 23 '17 at 22:47
  • It's not only selecting the visible cells, self.selectedIndexPath has the random index. When scrolling to that selected indexpath the cell will automatically be selected. Try downloading the sample project and test...it contains the answer – Jayaraj Jan 24 '17 at 03:58
  • Add these two lines of code **[self.myCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:true]; [_myCollectionView reloadData];** – Jayaraj Jan 24 '17 at 09:45
  • First, your original answer is indeed incorrect. It is only selecting the visible cells. Second, i won't just download any zip file from any stranger on internet. And third, i don't want to call reload. The reload will basically call cellForItemAtIndexPath for all the visible cells. It does work but it lose all the benefit prefetching gives you and it's even more expensive than disable the prefetching. This issue only occurs when prefetching is enabled. My original code works perfectly fine with prefetching disabled. – dknyxh Jan 24 '17 at 17:00