UICollectionVer el desplazamiento horizontal, borrando el último elemento, la animation no funciona

Tengo una UICollectionView. Se desplaza horizontalmente, tiene solo una fila de elementos y se comporta como una pagination UIScrollView. Estoy haciendo algo a lo largo de las líneas del selector de tabs de Safari, para que puedas ver el borde de cada elemento. Solo tengo una seccion

Si borro un elemento que no es el último, todo funciona como se esperaba y un nuevo elemento se desliza desde la derecha.

Si borro el último elemento, la position de desplazamiento de la vista de colección salta al elemento N-1 (no se anima suavemente), y luego veo desaparecer el elemento N (el que eliminé).

Este comportamiento no está relacionado con el layout personalizado que hice, ya que se produce incluso si lo cambio para usar un layout de flujo simple. Estoy eliminando los elementos usando:

[self.tabCollectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]]; 

Alguien más ha experimentado esto? ¿Es un error en UICollectionView, y hay una solución?

UICollectionViewFlowLayout que mi implementación funcionara utilizando el estándar UICollectionViewFlowLayout . Tuve que crear las animaciones manualmente.

Primero, causé que la celda eliminada se desvanezca usando una animation básica:

 - (void)tappedCloseButtonOnCell:(ScreenCell *)cell { // We don't want to close our last screen. if ([self screenCount] == 1u) { return; } [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^{ // Fade out the cell. cell.alpha = 0.0f; } completion:^(BOOL finished) { NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell]; UIViewController *screen = [self viewControllerAtIndex:indexPath.item]; [self removeScreen:screen animated:YES]; }]; } 

A continuación, causé que la vista de colección se desplazara a la celda anterior. Una vez que he desplazado a la celda deseada, elimino la celda eliminada.

 - (void)removeScreen:(UIViewController *)screen animated:(BOOL)animated { NSParameterAssert(screen); NSInteger index = [[self.viewControllerDictionaries valueForKeyPath:kViewControllerKey] indexOfObject:screen]; if (index == NSNotFound) { return; } [screen willMoveToParentViewController:nil]; if (animated) { dispatch_time_t popTime = DISPATCH_TIME_NOW; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; // Disables user interaction to make sure the user can't interact with // the collection view during the time between when the scroll animation // ends and the deleted cell is removed. [self.collectionView setUserInteractionEnabled:NO]; // Scrolls to the previous item, if one exists. If we are at the first // item, we just let the next screen slide in from the right. if (index > 0) { popTime = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC); NSIndexPath *targetIndexPath = [NSIndexPath indexPathForItem:index - 1 inSection:0]; [self.collectionView scrollToItemAtIndexPath:targetIndexPath atScrollPosition:UICollectionViewScrollPositionCentenetworkingHorizontally animated:YES]; } // Uses dispatch_after since -scrollToItemAtIndexPath:atScrollPosition:animated: // doesn't have a completion block. dispatch_after(popTime, dispatch_get_main_queue(), ^{ [self.collectionView performBatchUpdates:^{ [self.viewControllerDictionaries removeObjectAtIndex:index]; [self.collectionView deleteItemsAtIndexPaths:@[indexPath]]; [screen removeFromParentViewController]; [self.collectionView setUserInteractionEnabled:YES]; } completion:NULL]; }); } else { [self.viewControllerDictionaries removeObjectAtIndex:index]; [self.collectionView reloadData]; [screen removeFromParentViewController]; } self.addPageButton.enabled = YES; [self postScreenChangeNotification]; } 

La única parte que es un poco cuestionable es dispatch_after() . Desafortunadamente, -scrollToItemAtIndexPath:atScrollPosition:animated: no tiene un bloque de finalización, así que tuve que simularlo. Para evitar problemas de time, desactivé la interacción del usuario. Esto evita que el usuario interactúe con la vista de colección antes de eliminar la celda.

Otra cosa que tuve que tener en count es que debo restablecer el alfa de mi celda a 1 debido a la reutilización de la celda.

Espero que esto te ayude con tu selector de tabs estilo Safari. Sé que su implementación es diferente a la mía, y espero que mi solución funcione para usted también.

Sé que esto ya tiene una respuesta, pero implementé esto de una manera ligeramente diferente que no requiere despacho después de un intervalo establecido.

En su método de eliminación, haría una comprobación para determinar si se estaba eliminando el último elemento. Si fue llamada, haga lo siguiente:

 if(self.selection == self.assets.count-1 && self.selection != 0){ isDeleting = YES; [collection scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.selection-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES]; } 

Asumiendo que la selección es el elemento seleccionado que está eliminando. Esto se desplazará al elemento a la izquierda del mismo. Observe la statement si comprobando que este no es el único elemento. Si fuera así, la llamada se bloquearía ya que no hay -1 fila.

Luego puede implementar el siguiente método al que se llama cuando se completa la animation de desplazamiento. Simplemente establezco isDeleting en no en el método deleteObjectInCollection y todo parece funcionar.

 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{ if(isDeleting){ [self deleteObjectInCollection]; } } 

Espero que esto ayude.

Y aquí hay otra solución (targetIndexPath es el índicePath de la celda que se eliminará). Este código simplemente se puede colocar en un método removeCellAtIndexPath: (NSIndexPath *) targetIndexPath y ya está (asumiendo que mis adaptaciones de mi código a código público son correctas, de lo contrario pregúnteme e intentaré ayudar).

 // (Here it's assumed that self.collectionView.collectionViewLayout has been assigned a UICollectionViewFlowLayout before; note the word "FLOW" in that class name) UICollectionViewFlowLayout* layout = (UICollectionViewFlowLayout*) self.collectionView.collectionViewLayout; // Find index path of the last cell in collection view: NSIndexPath* lastIndexPath = [self lastItemIndexPath]; // Extend content area temporarily to fill out space left by the last row after deletion, if last row is visible: BOOL lastRowWasVisible = NO; if ([self.collectionView icn_cellAtIndexPathIsVisible:lastIndexPath]) { lastRowWasVisible = YES; // Adapt section spacing to temporarily fill out the space potentially left after removing last cell: CGFloat cellWithLineSpacingHeight = 79.0f + 8.0f; // Height of my cell + one line spacing layout.sectionInset = UIEdgeInsetsMake(0.0f, 0.0f, cellWithLineSpacingHeight, 0.0f); } // Remove the cell: [self.collectionView performBatchUpdates:^{ [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:targetIndexPath]]; } completion:^(BOOL finished) { // Only scroll if we had the last row visible: if (lastRowWasVisible) { NSIndexPath* lastItemIndexPath = [self lastItemIndexPath]; // Run a custom scroll animation for two reasons; 1. that way we can reset the insets when animation is finished, and 2. using the "animated:YES" option lags here for some reason: [UIView animateWithDuration:0.3f animations:^{ [self.collectionView scrollToItemAtIndexPath:prevItemIndexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:NO]; } completion:^(BOOL finished) { // Reset the space placeholder once having scrolled away from it: layout.sectionInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f); }]; } }]; 

El icn_cellAtIndexPathIsVisible: método es solo una categoría en UICollectionView:

 - (BOOL) icn_cellAtIndexPathIsVisible:(NSIndexPath*)indexPath { BOOL __block wasVisible = NO; [self.indexPathsForVisibleItems enumerateObjectsUsingBlock:^(NSIndexPath* ip, NSUInteger idx, BOOL *stop) { if ([ip isEqual:indexPath]) { wasVisible = YES; *stop = YES; } }]; return wasVisible; } 

Actualización: esto solo funciona con una sección.

Una solución económica es agregar otra celda 0px como la última celda y nunca eliminarla.

 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { if indexPath.row < collectibles.count - 1 { // dont delete the last one. just because the animation isn't working collectibles.removeAtIndex(indexPath.row) collectionView.deleteItemsAtIndexPaths([indexPath]) } } 

Estaba enfrentando un problema similar con una sola línea de desplazamiento horizontal a través de la vista de colección de imágenes. El problema desapareció cuando eliminé mi código para configurar el contentSize la vista de colección, parece manejar esto automágicamente.

No es una respuesta particularmente prolija, pero espero que ayude.