2012-02-07 11 views
6

Tengo un UITableView que presenta algunas configuraciones para el usuario. Algunas celdas están ocultas a menos que un UISwitch esté en la posición 'On'. Tengo el siguiente código:Mostrar y ocultar UITableViewCell con UISwitch bloqueos demasiado rápidos

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return switchPush.on ? 6 : 1; 
} 

// Hooked to the 'Value Changed' action of the switchPush 
- (IBAction)togglePush:(id)sender { 
    NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:0]; 
    for(int i = 1; i <= 5; i++) { 
     [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]]; 
    } 

    [tableView beginUpdates]; 
    if(switchPush.on) { 
     [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; 
    } else { 
     [tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; 
    } 
    [tableView endUpdates]; 
} 

Esto funciona como se espera, hasta que me cambio la UISwitch dos veces en rápida sucesión (pulsando dos veces), en cuyo caso la aplicación se bloquea con un

Invalid table view update. The application has requested an update to the table view that is inconsistent with the state provided by the data source. 

Sé que es causado por el valor de retorno incorrecto de numberOfRowsInSection ya que el interruptor está de regreso en su posición original mientras la animación de la celda todavía se está reproduciendo. Intenté desactivar el alternar y conectar el código con otros controladores de eventos, pero nada parece evitar el bloqueo. El uso de reloadData en lugar de la animación también resuelve el problema, pero preferiría las agradables animaciones.

¿Alguien sabe una forma correcta de implementar esto?

+0

¿Qué es un ITUoggle? ¿Te refieres a un 'UISwitch'? – Mundi

+0

Sí lo hice, corregí la pregunta. –

Respuesta

2

Simplemente configure la propiedad enabled de su interruptor en NO hasta que se completen las actualizaciones.

+2

He intentado esto, pero todavía es posible cambiar el interruptor hacia atrás antes de que la desactivación surta efecto. –

+0

Agregue un nuevo '@ selector' para el evento' UIControlEventTouchDown' y desactívelo allí. – Mundi

+0

¿Lo hiciste funcionar de esta manera? Por favor, considere marcar la marca de verificación. – Mundi

2

Tuve este problema en el mío y la forma de evitar el bloqueo es no utilizar explícitamente el interruptor u, en su lugar, transmitir la información a un booleano, aquí está cómo lo hice.

Añadir un booleano a la parte superior de su archivo de aplicación

bool _showRows = NO; 

actualizar el código uiswitch

- (IBAction)SwitchDidChange:(id)sender { 

NSArray *aryTemp = [[NSArray alloc] initWithObjects:[NSIndexPath indexPathForRow:1 inSection:0], 
        [NSIndexPath indexPathForRow:2 inSection:0], 
        [NSIndexPath indexPathForRow:3 inSection:0], 
        [NSIndexPath indexPathForRow:4 inSection:0],nil]; 

if (_showRows) { 
    _showRows = NO; 
    _switch.on = NO; 
    [_tblView deleteRowsAtIndexPaths:aryTemp withRowAnimation:UITableViewRowAnimationTop]; 
} 
else { 
    _showRows = YES; 
    _switch.on = YES; 
    [_tblView insertRowsAtIndexPaths:aryTemp withRowAnimation:UITableViewRowAnimationBottom]; 
} 
} 

Y, por último actualizar su numberOfRowsInSection

- (NSInteger)tableView:(UITableView *)tableView 
numberOfRowsInSection:(NSInteger)section 
{ 
if (section == 0) { 

    if (_showRows) { 
     return 5; 
    } 
    else { 
     return 1; 

    }  
} 
return 0; 
} 
3

Otra solución (más elegante) en el problema es este:

que modificó el método de - (IBAction)SwitchDidChange:(id)sender Alan MacGregor de esta manera:

- (IBAction)SwitchDidChange:(UISwitch *)source { 
    if (_showRows != source.on) { 
     NSArray *aryTemp = [[NSArray alloc] initWithObjects: 
        [NSIndexPath indexPathForRow:1 inSection:0], 
        [NSIndexPath indexPathForRow:2 inSection:0], 
        [NSIndexPath indexPathForRow:3 inSection:0], 
        [NSIndexPath indexPathForRow:4 inSection:0],nil]; 
     [_tblView beginUpdates]; 
     _showRows = source.on; 
     if (_showRows) { 
      [_tblView insertSections:aryTemp withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     else { 
      [_tblView deleteSections:aryTemp withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     [_tblView endUpdates]; 
    } 
} 

Las otras partes permanecen sin cambios.

0

Los eventos UIControlEventValueChanged se producen incluso cuando el valor de un control no cambia realmente. así que se llama a togglePush incluso cuando el valor del interruptor no cambia. cuando se activa rápidamente el interruptor, es posible que no siempre vaya desde> apagado> activado> desactivado, etc. es posible activarlo> activado> activado> desactivado.

así que lo que está sucediendo es que estás recibiendo dos ons seguidos causando dos InsertSections uno después del otro. que obviamente es malo

Para solucionar esto, debe recordar cuál era el estado anterior del botón (en un ivar, tal vez) y solo realizar la inserción (o eliminar) si el nuevo valor (source.on) es diferente del anterior valor.