UICollectionView单元格select和单元重用

在select细胞时,我想要处理改变细胞的外观。 我想到了委托方法collectionView:didSelectItemAtIndexPath:collectionView:didDeselectItemAtIndexPath:是我应该在哪里编辑单元格。

 -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { DatasetCell *datasetCell = (DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath]; [datasetCell replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]]; datasetCell.backgroundColor = [UIColor skyBlueColor]; } 

 -(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { DatasetCell *datasetCell = (DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath]; [datasetCell replaceHeaderGradientWith:[UIColor grayGradient]]; datasetCell.backgroundColor = [UIColor myDarkGrayColor]; } 

这工作正常,除了单元格被重用。 如果我在索引(0,0)处select单元格,它会改变外观,但是当我向下滚动时,在选定状态中会有另一个单元格。

我相信我应该使用UICollectionViewCell方法-(void)prepareForReuse准备重用单元格(即将单元格外观设置为非选中状态),但是这给我带来了困难。

 -(void)prepareForReuse { if ( self.selected ) { [self replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]]; self.backgroundColor = [UIColor skyBlueColor]; } else { [self replaceHeaderGradientWith:[UIColor grayGradient]]; self.backgroundColor = [UIColor myDarkGrayColor]; } } 

当我回滚到顶部时,索引(0,0)处的单元处于取消select状态。

当我刚使用cell.backgroundView属性,以防止发生这种情况是:

 -(void)prepareForReuse { self.selected = FALSE; } 

并且select状态按预期工作。

有任何想法吗?

你的观察是正确的。 由于单元的重用,此行为正在发生。 但是你不必用prepareForReuse做任何事情。 取而代之的是在cellForItem中检查并相应地设置属性。 就像是..

  - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cvCell" forIndexPath:indexPath]; if (cell.selected) { cell.backgroundColor = [UIColor blueColor]; // highlight selection } else { cell.backgroundColor = [UIColor redColor]; // Default color } return cell; } -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath]; datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection } -(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath]; datasetCell.backgroundColor = [UIColor redColor]; // Default color } 

一旦您设置了单元格的backgroundViewselectedBackgroundView框架将处理切换视图 ,请参阅pipe理视觉状态以进行select和突出显示的示例:

 UIView* backgroundView = [[UIView alloc] initWithFrame:self.bounds]; backgroundView.backgroundColor = [UIColor redColor]; self.backgroundView = backgroundView; UIView* selectedBGView = [[UIView alloc] initWithFrame:self.bounds]; selectedBGView.backgroundColor = [UIColor whiteColor]; self.selectedBackgroundView = selectedBGView; 

你只需要在你的类中实现UICollectionViewDelegate使单元格高亮和select像这样:

 - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath; { return YES; } 

这对我有用。

UICollectionView在iOS 10中发生了一些变化,导致了上面的解决scheme。

这是一个很好的指导: https : //littlebitesofcocoa.com/241-uicollectionview-cell-pre-fetching

离开屏幕后,电池现在停留一段时间。 这意味着有时我们可能无法获得didDeselectItemAt indexPath中的单元格以调整它。 它然后可以在屏幕上显示未更新和未回收。 prepareForReuse不帮助这个angular落的情况下。

最简单的解决方法是通过将isPrefetchingEnabled设置为false来禁用新的滚动。 有了这个,使用cellForItemAt didSelect didDeselectpipe理单元格的显示cellForItemAt didDeselect工作了。

但是,如果您希望保持新的平滑滚动行为,最好使用willDisplay

 func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { let customCell = cell as! CustomCell if customCell.isSelected { customCell.select() } else { customCell.unselect() } } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell //Don't even need to set selection-specific things here as recycled cells will also go through willDisplay return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let cell = collectionView.cellForItem(at: indexPath) as? CustomCell cell?.select() } func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { let cell = collectionView.cellForItem(at: indexPath) as? CustomCell cell?.unselect() // <----- this can be null here, and the cell can still come back on screen! } 

使用上述方法,您可以控制选中的单元格,取消选中屏幕,回收并重新显示。

阿尼尔是在正确的轨道(他的解决scheme看起来应该工作,我独立于他开发这个解决scheme)。 我仍然使用prepareForReuse:方法来设置单元格的selectedFALSE ,然后在cellForItemAtIndexPath我检查单元格的索引是否在`collectionView.indexPathsForSelectedItems',如果是的话,突出显示它。

在自定义单元格中:

 -(void)prepareForReuse { self.selected = FALSE; } 

cellForItemAtIndexPath:处理突出显示和取消重新使用重用单元格:

 if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) { [collectionView selectItemAtIndexPath:indexPath animated:FALSE scrollPosition:UICollectionViewScrollPositionNone]; // Select Cell } else { // Set cell to non-highlight } 

然后在didDeselectItemAtIndexPath:didSelectItemAtIndexPath:处理单元格突出显示和取消didDeselectItemAtIndexPath: didSelectItemAtIndexPath:

这对我来说就像一个魅力。

我有一个水平滚动集合视图(我在Tableview中使用集合视图),我也遇到了重复使用的问题,每当我select一个项目并向右滚动时,下一个可见集合中的一些其他单元格会自动select。 试图解决这个使用任何自定义单元格属性,如“select”,突出显示等不帮我,所以我想出了下面的解决scheme,这对我工作。

步骤1:

在collectionView中创build一个variables来存储select的索引,这里我使用了一个名为selectedIndex的类variables

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { MyCVCell *cell = (MyCVCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"MyCVCell" forIndexPath:indexPath]; // When scrolling happens, set the selection status only if the index matches the selected Index if (selectedIndex == indexPath.row) { cell.layer.borderWidth = 1.0; cell.layer.borderColor = [[UIColor redColor] CGColor]; } else { // Turn off the selection cell.layer.borderWidth = 0.0; } return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath]; // Set the index once user taps on a cell selectedIndex = indexPath.row; // Set the selection here so that selection of cell is shown to ur user immediately cell.layer.borderWidth = 1.0; cell.layer.borderColor = [[UIColor redColor] CGColor]; [cell setNeedsDisplay]; } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath]; // Set the index to an invalid value so that the cells get deselected selectedIndex = -1; cell.layer.borderWidth = 0.0; [cell setNeedsDisplay]; } 

-anoop

在您的自定义单元格创build公共方法:

 - (void)showSelection:(BOOL)selection { self.contentView.backgroundColor = selection ? [UIColor blueColor] : [UIColor white]; } 

另外写-prepareForReuse cell方法的redefenition:

 - (void)prepareForReuse { [self showSelection:NO]; [super prepareForReuse]; } 

而在你的ViewController中,你应该有_selectedIndexPathvariables,它在-didSelectItemAtIndexPath中定义,并在-didDeselectItemAtIndexPath

 NSIndexPath *_selectedIndexPath; - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath]; if (_selectedIndexPath) { [cell showSelection:[indexPath isEqual:_selectedIndexPath]]; } } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; [cell showSelection:![indexPath isEqual:_selectedIndexPath]];// on/off selection _selectedIndexPath = [indexPath isEqual:_selectedIndexPath] ? nil : indexPath; } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; [cell showSelection:NO]; _selectedIndexPath = nil; } 

只有@ stefanB解决scheme在iOS 9.3上为我工作

在这里,我必须改变Swift 2

 func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { //prepare your cell here.. //Add background view for normal cell let backgroundView: UIView = UIView(frame: cell!.bounds) backgroundView.backgroundColor = UIColor.lightGrayColor() cell!.backgroundView = backgroundView //Add background view for selected cell let selectedBGView: UIView = UIView(frame: cell!.bounds) selectedBGView.backgroundColor = UIColor.redColor() cell!.selectedBackgroundView = selectedBGView return cell! } func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool { return true } func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool { return true } 

您可以将单元格的selectedBackgroundView设置为backgroundColor = x。

现在,每当你点击单元格时,他所select的模式将自动改变,并将变为背景颜色。

感谢您的回复@ RDC 。

以下代码适用于Swift 3

 // MARK: - UICollectionViewDataSource protocol func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { //prepare your cell here.. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCell cell.myLabel.text = "my text" //Add background view for normal cell let backgroundView: UIView = UIView(frame: cell.bounds) backgroundView.backgroundColor = UIColor.lightGray cell.backgroundView = backgroundView //Add background view for selected cell let selectedBGView: UIView = UIView(frame: cell.bounds) selectedBGView.backgroundColor = UIColor.green cell.selectedBackgroundView = selectedBGView return cell } // MARK: - UICollectionViewDelegate protocol func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { return true } func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { return true } 

更改单元格属性(如单元格的背景颜色)不应该在UICollectionViewController本身上完成,它应该在你的CollectionViewCell类中完成。 不要使用didSelect和didDeselect,只是使用这个:

 class MyCollectionViewCell: UICollectionViewCell { override var isSelected: Bool { didSet { // Your code } } } 

我做了什么来解决这个问题是在自定义的单元格中进行更改。 你有一个名为DataSetCell的自定义单元,在你的类中你可以做下面的事情(代码是在swift中)

 override var isSelected: Bool { didSet { if isSelected { changeStuff } else { changeOtherStuff } } } 

这样做是每次单元格被选中,取消select,初始化或从可重用的队列中调用,该代码将运行,并将作出更改。 希望这可以帮助你。