2011-05-10 11 views
25

Estoy interesado en cómo tweetbot hace lo siguiente:Cómo crear una barra de herramientas entre las filas UITableView

Enter image description here

Me gustaría crear lo mismo con mi aplicación, en la que si se hace clic en una fila, aparece una UIToolBar adicional y al presionar en cualquier otra fila se descartará esta vista con animaciones.

Creo que la lógica es simple, solo tiene que agregar un subView a UITableViewCell cuando se presiona y desplazar el resto del contenido, pero ¿cómo lo descarta cuando presiono la otra fila?

Respuesta

18

En tableView:didSelectRowAtIndexPath:, se elimina la vista de herramientas de la última celda seleccionada. Si no existe tal vista, usted crea una nueva. Luego agrega esta vista a la celda recién seleccionada. Guarde el indexPath de la fila seleccionada.

En tableView:heightForRowAtIndexPath:, compruebe si indexPath es el mismo que el indexPath guardado. Si son iguales, devuelve una altura que es el alto de ambas vistas. Si no es igual, simplemente devuelve la altura de la "celda real".

Ponga todas sus llamadas en didSelectRowAtIndexPath entre [tableView beginUpdates] y [tableView endUpdates] para obtener la animación para el cambio de altura.

+0

Creo que este es un mejor diseño que la mención donde se debe cambiar el origen de datos. – Gary

+0

así que básicamente va a añadir un nuevo punto de vista aquí en este lugar de una nueva fuente de datos, esto es lo que tenía en mente inicialmente .. algo más de código sería genial ... basado en mi entendimiento va a añadir esta vista para cada celular en la mesa? – aherlambang

+0

Crearía una vista y la eliminaría de la celda seleccionada anterior cada vez antes de agregarla a la nueva celda. –

1

No agregaría una subvista a UITableViewCell, agregaría otra fila al UITableView. De esta forma, el UITableView se encargará de la animación. (Y yo no creo que eso sea posible animada cambia UITableViewCell altura ...)

Uso simplemente

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 

hacer añadir una fila. Y

- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 

para quitarlo.

+0

¡Y también necesita cambiar sus métodos de origen de datos para devolver su nueva fila! – gcamp

+0

Y reconoce manejar el toque en el resto de las celdas mientras se muestra esta celda. ;-) –

+0

insertRowsAtIndexPath solo especifica dónde se inserta, pero no responde a lo que se inserta ... entonces, ¿cómo especifico qué se inserta? – aherlambang

0

Esto es lo que estoy haciendo para que la animación esté limpia.

Además de la estrategia de usuario Fluchtpunkt sugiere (es decir, la adición de una vista secundaria de la célula, la actualización de la altura de la celda a través de heightForRowAtIndexPath, beginUpdates y endUpdates), estoy encontrando las siguientes medidas para ser útil con la animación:

  1. Las tableviewcells tienen una imagen de fondo. De lo contrario, la subvista/barra de herramientas agregada es visible a través de la celda justo antes de que la vista de tabla anime el cambio de altura.
  2. La tableviewcell está 'detrás' de la vista que es la celda debajo de ella, de lo contrario, la subvista/barra de herramientas se mostrará demasiado pronto. Estoy usando [tableview sendSubviewToBack: cell]; y se está ocupando de eso.

Esto está limpio para mí, pero no exactamente como Tweetbot. Curiosamente, la animación de Tweetbot parece tirar de la barra de herramientas hacia abajo mientras la parte inferior de la celda se anima hacia abajo. Parece que se debe realizar alguna animación adicional, o mi teoría de la conspiración es que en realidad está agregando la subvista a la parte superior de la celda debajo de la celda seleccionada, y luego realizando el alargamiento y la animación en la celda de abajo.

+0

Sí, he planchado los problemas de animación. Mi método es algo similar a la suya, todavía no puedo conseguir que se vea totalmente como lo tweetbots tienen – aherlambang

29

La mejor manera de hacerlo es agregar una celda ficticia debajo de la celda que se tocó.

Primero debe realizar un seguimiento de qué celda se ha intervenido y actuar en consecuencia.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

    //if user tapped the same row twice let's start getting rid of the control cell 
    if([indexPath isEqual:self.tappedIndexPath]){ 
     [tableView deselectRowAtIndexPath:indexPath animated:NO]; 
    } 

    //update the indexpath if needed... I explain this below 
    indexPath = [self modelIndexPathforIndexPath:indexPath]; 

    //pointer to delete the control cell 
    NSIndexPath *indexPathToDelete = self.controlRowIndexPath; 

    //if in fact I tapped the same row twice lets clear our tapping trackers 
    if([indexPath isEqual:self.tappedIndexPath]){ 
     self.tappedIndexPath = nil; 
     self.controlRowIndexPath = nil; 
    } 
    //otherwise let's update them appropriately 
    else{ 
     self.tappedIndexPath = indexPath; //the row the user just tapped. 
     //Now I set the location of where I need to add the dummy cell 
     self.controlRowIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section]; 
    } 

    //all logic is done, lets start updating the table 
    [tableView beginUpdates]; 

    //lets delete the control cell, either the user tapped the same row twice or tapped another row 
    if(indexPathToDelete){ 
     [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] 
          withRowAnimation:UITableViewRowAnimationNone]; 
    } 
    //lets add the new control cell in the right place 
    if(self.controlRowIndexPath){ 
     [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath] 
          withRowAnimation:UITableViewRowAnimationNone]; 
    } 

    //and we are done... 
    [tableView endUpdates]; 
} 

cada vez que tenga esa célula ficticia presente usted tiene que asegurarse de que envíe la cuenta correcta.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    if(self.controlRowIndexPath){ 
     return modelArray.count + 1; 
    } 
    return self.modelArray.count; 
} 

Además, devolver la altura apropiada para su ControlCell.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if([indexPath isEqual:self.controlRowIndexPath]){ 
     return 45; //height for control cell 
    } 
    return 70; //height for every other cell 
} 

Por último, recordar la célula de control es un maniquí. No es parte del modelo, por lo tanto, tienes que dar cuenta de eso. Si el usuario toca una fila que está encima, la última fila girada está bien, pero cuando la nueva fila golpeada está debajo de esa celda de control, debe asegurarse de acceder a la fila derecha de su modelo. En otras palabras, cuenta para esa celda falsa en el medio de tu vista.

- (NSIndexPath *)modelIndexPathforIndexPath:(NSIndexPath *)indexPath 
{ 
    int whereIsTheControlRow = self.controlRowIndexPath.row; 
    if(self.controlRowIndexPath != nil && indexPath.row > whereIsTheControlRow) 
     return [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0]; 
    return indexPath; 
} 

Espero que esto ayude.

+0

Esto es correcto .... Sé que la mayoría no tendrá que añadir +1 a su recuento ya que esto sólo tiene que añadir que celda actual a continuación y parece tonto. Pero si tiene una celda personalizada puede usar setSelected: (BOOL) animado seleccionado: (BOO) método animado para agregar una imagen agradable y botones como lo hace tweetbot ... Espero haber aclarado algunas cosas – FreeAppl3

+0

¿Cómo maneja el célula en cellForRowAtIndexPath? Observé que el código anterior es el mismo que en la sesión de tablas de WWDC desde 2011, ¿tiene el código de muestra de esa sesión? THX –

+2

Básicamente: 'Si (self.controlRowIndexPath && [indexPath IsEqual: self.controlRowIndexPath])'> devuelve la celda para el menú de control. Sin código fuente, lo siento. Presté mucha atención a la sesión WWDC y resolví el resto por prueba/error. – rjgonzo

0

@rjgonzo esto funciona muy bien, pero hay un problema menor sobre cómo mantener el indexPathToDelete. Como es solo otro puntero a self.controlRowIndexPath, una vez que borre o reasigne el self.controlRowIndexPath, indexPathToDelete no será lo que usted quería, y tableView deleteRowsAtIndexPaths: withRowAnimation: call, obtendrá un bloqueo de SIGBART.

así, en lugar de

//pointer to delete the control cell 
    NSIndexPath *indexPathToDelete = self.controlRowIndexPath; 

y

//lets delete the control cell, either the user tapped the same row twice or tapped another row 
    if(indexPathToDelete){ 
     [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] 
         withRowAnimation:UITableViewRowAnimationNone]; 
    } 

el siguiente código debería funcionar bien: Código

//pointer to delete the control cell 
NSIndexPath *indexPathToDelete = [NSIndexPath indexPathForRow:self.control_row_index_path.row inSection:self.control_row_index_path.section]; 
    ... 
    ... 
    //lets delete the control cell, either the user tapped the same row twice or tapped another row 
if(indexPathToDelete.row != 0){ 
    NSLog(@"row to delete %d", indexPathToDelete.row); 
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] withRowAnimation:UITableViewRowAnimationNone]; 
} 
2

de rjgonzo funciona bien, excepto para el caso en el que sólo tiene 1 fila en la vista de tabla. Cuando solo hay 1 fila (y 1 objeto en el modelo de datos tableview) obtendrá una excepción NSRange cuando llame a insertRowsatIndexPath (s). Para solucionar esto, verifiqué si el modelo de datos solo tenía 1 objeto y, de ser así, agregué la fila al camino de índice actual (en lugar de controlindexpath), lo que resulta en que la fila se agrega lógicamente encima de la primera fila y luego invoco moveRowAtIndexPath para intercambie las filas después de llamar a [self.tableView endUpdates]. La animación se muestra como se esperaba con la fila de control que parece deslizarse hacia abajo desde la primera fila.

if(self.controlRowIndexPath){ 

//Check to see if the datamodel only has 1 object 

if([self.objects count]==1){ 
//If so then insert the row above the row 
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
           withRowAnimation:UITableViewRowAnimationNone]; 

    } 
    else 
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath] 
          withRowAnimation:UITableViewRowAnimationNone]; 

} 

[self.tableView endUpdates]; 

//if the row was inserted above the 1st row then switch the rows 

if([self.objects count]==1) 
[self.tableView moveRowAtIndexPath:self.controlRowIndexPath toIndexPath:indexPath]; 
+0

Esto no solo no fue necesario, sino que realmente rompió el comportamiento deseado. Yo tenía un parecido "sólo un elemento, NSRange" problema, pero después de intentar este código, me di cuenta de que el problema era que yo estaba usando indexPath en un lugar que debería haber estado usando self.tappedIndexPath en el método cellForIndexPath. –

Cuestiones relacionadas