ios objective-c uicollectionview

ios - Selección de celda UICollectionView y reutilización de celda



objective-c (11)

Tras la selección de celda, quiero manejar el cambio de la apariencia de la celda. Pensé que el método de delegado collectionView:didSelectItemAtIndexPath: & collectionView:didDeselectItemAtIndexPath: es donde debería editar la celda.

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

y

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

Esto funciona bien, excepto cuando la celda se vuelve a usar. Si selecciono celda en el índice (0, 0), cambia la apariencia, pero cuando me desplazo hacia abajo, hay otra celda en el estado seleccionado.

Creo que debería usar el método UICollectionViewCell -(void)prepareForReuse para preparar la celda para volver a usarla (es decir, configurar la apariencia de la celda como estado no seleccionado) pero me da dificultades.

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

Cuando me desplazo de nuevo a la parte superior, la celda en el índice (0, 0) está en el estado deseleccionado.

Cuando acabo de utilizar la propiedad cell.backgroundView, para evitar que esto ocurriera era para:

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

y el estado de selección funcionó según lo previsto.

¿Algunas ideas?


Anil estaba en el camino correcto (su solución parece que debería funcionar, desarrollé esta solución independientemente de la suya). Todavía utilicé el método prepareForReuse: para establecer la celda selected en FALSE , luego en cellForItemAtIndexPath , compruebo si el índice de la celda está en `collectionView.indexPathsForSelectedItems '', de ser así, resáltalo.

En la celda personalizada:

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

En cellForItemAtIndexPath: para manejar el resaltado y la dehighlighting reutilizar celdas:

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

Y luego maneje el resaltado de células y la iluminación descendente en didDeselectItemAtIndexPath: y didSelectItemAtIndexPath:

Esto funciona como un encanto para mí.


El cambio de la propiedad de la celda, como los colores de fondo de la celda, no debe realizarse en el UICollectionViewController en sí mismo, sino dentro de la clase CollectionViewCell. No use didSelect y didDeselect, solo use esto:

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


En tu celda personalizada, crea el método público:

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

También escriba redefenition del método de celda -prepareForReuse:

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

Y en su ViewController debería tener _selectedIndexPath variable, que se definió en -didSelectItemAtIndexPath y se anuló en -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; }


Gracias a su respuesta @ RDC .

Los siguientes códigos funcionan con 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 }


Lo que hice para resolver esto fue hacer los cambios en la celda personalizada. Usted tiene una celda personalizada llamada DataSetCell en su clase, puede hacer lo siguiente (el código es rápido)

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

Lo que hace es que cada vez que se selecciona, se deselecciona, se inicializa o se llama a la célula desde la cola reutilizable, ese código se ejecutará y se realizarán los cambios. Espero que esto te ayude.


Solo la solución @ stefanB funcionó para mí en iOS 9.3

Aquí lo que tengo que cambiar para 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 }


Tenía una vista de colección de desplazamiento horizontal (uso la vista de colección en Tableview) y también tuve problemas con la reutilización de la celda, cada vez que selecciono un elemento y me desplazo hacia la derecha, algunas otras celdas en el siguiente conjunto visible se seleccionan automáticamente. Tratar de resolver esto usando cualquier propiedad de celda personalizada como "seleccionado", resaltado, etc. no me ayudó, así que se me ocurrió la siguiente solución y esto funcionó para mí.

Paso 1:

Crear una variable en la colecciónView para almacenar el índice seleccionado, aquí he usado una variable de nivel de clase llamada selectedIndex

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

-noop


Tu observación es correcta Este comportamiento está sucediendo debido a la reutilización de las celdas. Pero no tienes que hacer nada con el prepareForReuse . En su lugar, haga su verificación en cellForItem y establezca las propiedades en consecuencia. Algo como..

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


UICollectionView ha cambiado en iOS 10 presentando algunos problemas a las soluciones anteriores.

Aquí hay una buena guía: https://littlebitesofcocoa.com/241-uicollectionview-cell-pre-fetching

Las células ahora se quedan un poco después de salir de la pantalla. Lo que significa que a veces es posible que no podamos obtener una celda en didDeselectItemAt indexPath para ajustarla. Luego puede aparecer en la pantalla sin actualizar y sin reciclar. prepareForReuse no ayuda en este caso de esquina.

La solución más fácil es deshabilitar el nuevo desplazamiento configurando isPrefetchingEnabled en falso. Con esto, administrar la pantalla de la celda con cellForItemAt didSelect didDeselect funciona como solía hacerlo.

Sin embargo, si prefieres mantener el nuevo comportamiento de desplazamiento suave, es mejor utilizar 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! }

Con lo anterior, usted controla la celda cuando se selecciona, no se selecciona en la pantalla, se recicla y simplemente se vuelve a mostrar.


simplemente puede configurar la vista de Antecedentes seleccionada de la celda para que sea backgroundColor = x.

Ahora, cada vez que toque en la celda, su modo seleccionado cambiará automáticamente y cambiará al color de fondo para cambiar a x.


Framework se encargará de cambiar las vistas una vez que configure la vista de backgroundView y selectedBackgroundView su celda; consulte el ejemplo de Cómo administrar el estado visual para selecciones y aspectos destacados :

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;

solo necesita en su clase que implemente UICollectionViewDelegate enable cells para que se resalte y seleccione de esta manera:

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

Esto me funciona.