uitableviewdatasource uitableviewcontroller uitableviewcell example iphone cocoa-touch uitableview uikit

iphone - uitableviewcontroller - UITableView: borrar secciones con animación



uitableviewcontroller swift 4 (7)

Así que finalmente aquí está mi solución a este problema. Este método se puede aplicar a tablas de cualquier tamaño, cualquier número de secciones (por lo que puedo decir)

Como antes, he modificado el código de tabla de Matt Gallagher que coloca la lógica específica de la celda en un controlador de celda separado. Sin embargo, puede adaptar fácilmente este método a un modelo diferente

He agregado los siguientes (relevantes) ivars al código de Matt:

NSArray *allTableGroups; //always has a copy of every cell controller, even if filtered NSArray *filteredTableGroups; //always has a copy of the filtered table groups

El ivar original de Matt:

NSArray *allTableGroups

... siempre apunta a una de las matrices anteriores.

Esto probablemente se puede refactorizar y mejorar significativamente, pero no he tenido la necesidad. Además, si utiliza Core Data, NSFetchedResultsController lo hace más fácil.

Ahora en el método (estoy tratando de hacer un comentario tanto como pueda):

- (void)createFilteredTableGroups{ //Checking for the usual suspects. all which may through an exception if(model==nil) return; if(tableGroups==nil) return; if([tableGroups count]==0) return; //lets make a new array to work with NSMutableArray *newTableGroups = [[allTableGroups mutableCopy] autorelease]; //telling the table what we are about to do [self.tableView beginUpdates]; //array to track cells for deletion animation NSMutableArray *indexesToRemove = [NSMutableArray array]; //loop through each section for(NSMutableArray *eachSection in tableGroups){ //keeping track of the indexes to delete for each section NSMutableIndexSet *indexesForSection = [NSMutableIndexSet indexSet]; [indexesForSection removeAllIndexes]; //increment though cell indexes int rowIndex = 0; //loop through each cellController in the section for(ScheduleCellController *eachCellController in eachSection){ //Ah ha! A little magic. the cell controller must know if it should be displayed. //This you must calculate in your business logic if(![eachCellController shouldDisplay]){ //add non-displayed cell indexes [indexesForSection addIndex:rowIndex]; } rowIndex++; } //adding each array of section indexes, EVEN if it is empty (no indexes to delete) [indexesToRemove addObject:indexesForSection]; } //Now we remove cell controllers in newTableGroups and cells from the table //Also, each subarray of newTableGroups is mutable as well if([indexesToRemove count]>0){ int sectionIndex = 0; for(NSMutableIndexSet *eachSectionIndexes in indexesToRemove){ //Now you know why we stuck the indexes into individual arrays, easy array method [[newTableGroups objectAtIndex:sectionIndex] removeObjectsAtIndexes:eachSectionIndexes]; //tracking which cell indexPaths to remove for each section NSMutableArray *indexPathsToRemove = [NSMutableArray array]; int numberOfIndexes = [eachSectionIndexes count]; //create array of indexPaths to remove NSUInteger index = [eachSectionIndexes firstIndex]; for(int i = 0; i< numberOfIndexes; i++){ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:sectionIndex]; [indexPathsToRemove addObject:indexPath]; index = [eachSectionIndexes indexGreaterThanIndex:index]; } //delete the rows for this section [self.tableView deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationTop]; //next section please sectionIndex++; } } //now we figure out if we need to remove any sections NSMutableIndexSet *sectionsToRemove = [NSMutableIndexSet indexSet]; [sectionsToRemove removeAllIndexes]; int sectionsIndex = 0; for(NSArray *eachSection in newTableGroups){ //checking for empty sections if([eachSection count]==0) [sectionsToRemove addIndex:sectionsIndex]; sectionsIndex++; } //updating the table groups [newTableGroups removeObjectsAtIndexes:sectionsToRemove]; //removing the empty sections [self.tableView deleteSections:sectionsToRemove withRowAnimation:UITableViewRowAnimationTop]; //updating filteredTableGroups to the newTableGroups we just created self.filteredTableGroups = newTableGroups; //pointing tableGroups at the filteredGroups tableGroups = filteredTableGroups; //invokes the animation [self.tableView endUpdates]; }

Actualizar

He publicado mi solución a este problema como una respuesta a continuación. Toma un enfoque diferente de mi primera revisión.

Pregunta original Anteriormente hice una pregunta sobre SO que pensé que había resuelto mis problemas:

Cómo tratar con filas no visibles durante la eliminación de filas. (UITableViews)

Sin embargo, ahora tengo problemas similares nuevamente cuando elimino secciones de una UITableView. (resurgieron cuando cambié el número de secciones / filas en la tabla).

Antes de que te pierda debido a la longitud de corte de mi publicación, permíteme plantearte el problema con claridad, y puedes leer todo lo que necesites para dar una respuesta.

Problema:

Si el lote elimina filas y secciones de un UITableView, la aplicación falla, a veces. Depende de la configuración de la tabla y la combinación de filas y secciones que elijo eliminar.

El registro dice que se bloqueó porque dice que no actualicé correctamente la fuente de datos y la tabla:

Invalid update: invalid number of rows in section 5. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted).

Ahora, rápidamente, antes de escribir la respuesta obvia, le aseguro que de hecho he agregado y eliminado las filas y las secciones de DataSource correctamente. La explicación es larga, pero la encontrará más abajo, siguiendo el método.

Entonces con eso, si todavía estás interesado ...

Método que maneja la eliminación de secciones y filas:

- (void)createFilteredTableGroups{ //index set to hold sections to remove for deletion animation NSMutableIndexSet *sectionsToDelete = [NSMutableIndexSet indexSet]; [sectionsToDelete removeIndex:0]; //array to track cells for deletion animation NSMutableArray *cellsToDelete = [NSMutableArray array]; //array to track controllers to delete from presentation model NSMutableArray *controllersToDelete = [NSMutableArray array]; //for each section for(NSUInteger i=0; i<[tableGroups count];i++){ NSMutableArray *section = [tableGroups objectAtIndex:i]; //controllers to remove NSMutableIndexSet *controllersToDeleteInCurrentSection = [NSMutableIndexSet indexSet]; [controllersToDeleteInCurrentSection removeIndex:0]; NSUInteger indexOfController = 0; //for each cell controller for(ScheduleCellController *cellController in section){ //bool indicating whether the cell controller''s cell should be removed NSString *shouldDisplayString = (NSString*)[[cellController model] objectForKey:@"filteredDataSet"]; BOOL shouldDisplay = [shouldDisplayString boolValue]; //if it should be removed if(!shouldDisplay){ NSIndexPath *cellPath = [self indexPathOfCellWithCellController:cellController]; //if cell is on screen, mark for animated deletion if(cellPath!=nil) [cellsToDelete addObject:cellPath]; //marking controller for deleting from presentation model [controllersToDeleteInCurrentSection addIndex:indexOfController]; } indexOfController++; } //if removing all items in section, add section to removed in animation if([controllersToDeleteInCurrentSection count]==[section count]) [sectionsToDelete addIndex:i]; [controllersToDelete addObject:controllersToDeleteInCurrentSection]; } //copy the unfiltered data so we can remove the data that we want to filter out NSMutableArray *newHeaders = [tableHeaders mutableCopy]; NSMutableArray *newTableGroups = [[allTableGroups mutableCopy] autorelease]; //removing controllers int i = 0; for(NSMutableArray *section in newTableGroups){ NSIndexSet *indexesToDelete = [controllersToDelete objectAtIndex:i]; [section removeObjectsAtIndexes:indexesToDelete]; i++; } //removing empty sections and cooresponding headers [newHeaders removeObjectsAtIndexes:sectionsToDelete]; [newTableGroups removeObjectsAtIndexes:sectionsToDelete]; //update headers [tableHeaders release]; tableHeaders = newHeaders; //storing filtered table groups self.filteredTableGroups = newTableGroups; //filtering animation and presentation model update [self.tableView beginUpdates]; tableGroups = self.filteredTableGroups; [self.tableView deleteSections:sectionsToDelete withRowAnimation:UITableViewRowAnimationTop]; [self.tableView deleteRowsAtIndexPaths:cellsToDelete withRowAnimation:UITableViewRowAnimationTop]; [self.tableView endUpdates]; //marking table as filtered self.tableIsFiltered = YES; }

Mi conjetura:

El problema parece ser el siguiente: si miras arriba donde enumero la cantidad de celdas en cada sección, verás que la sección 5 parece aumentar en 1. Sin embargo, esto no es cierto. La sección original 5 se ha eliminado y otra sección ha ocupado su lugar (específicamente, es la antigua sección 10).

Entonces, ¿por qué la vista de tabla parece no darse cuenta de esto? Debe SABER que eliminé la sección anterior y no debería esperar que una nueva sección que ahora se encuentra en el índice de la sección anterior se vincule con el número de filas de la sección eliminada.

Esperemos que esto tenga sentido, es un poco complicado escribir esto.

(tenga en cuenta que este código funcionaba antes con un número diferente de filas / secciones. Esta configuración particular parece darle problemas)


Me he encontrado con este problema antes. Está intentando eliminar todas las filas de una sección y luego, además, esa sección ahora vacía. Sin embargo, es suficiente (y apropiado) eliminar solo esa sección. Todas las filas dentro de ella serán eliminadas también. Aquí hay un código de muestra de mi proyecto que maneja la eliminación de una fila. Debe determinar si debe eliminar solo esta fila de una sección o eliminar toda la sección si es la última fila restante en esa sección:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // modelForSection is a custom model object that holds items for this section. [modelForSection removeItem:[self itemForRowAtIndexPath:indexPath]]; [tableView beginUpdates]; // Either delete some rows within a section (leaving at least one) or the entire section. if ([modelForSection.items count] > 0) { // Section is not yet empty, so delete only the current row. [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else { // Section is now completely empty, so delete the entire section. [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade]; } [tableView endUpdates]; } }


Noté que primero está eliminando las secciones de la tabla y luego elimina las filas.

Sé que hay una discusión complicada sobre la inserción y eliminación de lotes para UITableViews en la Guía de programación de Table View, pero no cubre específicamente esto.

Creo que lo que está sucediendo es que al eliminar las secciones, las eliminaciones de filas se refieren a la fila incorrecta.

es decir, desea eliminar la sección n. ° 2 y la fila n. ° 1 de la sección n. ° 4 ... pero una vez que haya eliminado la sección n. ° 2, la sección anterior n. ° 4 es ahora la tercera, de modo que cuando elimine con la ruta NSIndexPath anterior (4, 1) está borrando una fila aleatoria diferente que puede no existir.

Así que creo que la solución podría ser tan simple como intercambiar esas dos líneas de código, por lo que primero se eliminan las filas y luego las secciones.


Sospecho que te estás olvidando de eliminar el objeto que representa la sección de tu almacenamiento interno, de modo que el método -numberOfSectionsInTableView: todavía devuelve 1 después de que se hayan eliminado todas las secciones.

¡Eso es exactamente lo que estaba haciendo mal cuando tuve el mismo choque!


Una forma mucho más simple de abordar esto es actualizar su fuente de datos, luego llamar a reloadSections

[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

Esto volverá a cargar una sola sección. Alternativamente, podría usar indexSetWithIndexesInRange: para cargar múltiples secciones simultáneamente.


Vi este mismo error exacto como resultado de la publicación prematura de la vista de fondo de mi celda de vista de tabla personalizada.

Con NSZombieEnabled recibí una excepción lanzada muy por debajo de una llamada interna a una función para preparar la celda para su reutilización. Sin NSZombieEnabled, obtenía el error de coherencia interno.

Por cierto, cuando arreglé el problema de retención / liberación en la vista de fondo de la celda, pude eliminar la última fila de la sección sin tener que eliminar la sección explícitamente.

Moraleja de la historia: este error simplemente significa que algo malo está sucediendo cuando intentas eliminar, y una de las cosas que sucede cuando eliminas es que la célula se prepara para ser reutilizada, así que si estás haciendo algo personalizado con tus celdas de tabla, mira para un posible error allí.


o simplemente haz esto

- (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if(editingStyle == UITableViewCellEditingStyleDelete) { //Delete the object from the table. [directoriesOfFolder removeObjectAtIndex:indexPath.row]; [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; } }

directorios de carpetas siendo su Matriz! ¡Eso es todo lo anterior, los códigos no me funcionaron! ¡Esto es menos costoso de hacer y simplemente tiene sentido!