2010-06-14 23 views
38

¿Cómo puedo agregar una UITableView en mi aplicación basada en vista donde el usuario tocará más de una celda y se seleccionará, exactamente como la configuración de "Nueva alarma" de la aplicación Reloj llamada "Repetir" (Reloj> Alarmas> +> Repetir), y ¿cómo puedo obtener todas las celdas seleccionadas en una matriz?UITableView Selección múltiple

+0

Psh. No vi el ejemplo de Apple sobre esto. –

Respuesta

27

En su implementación de -tableView:didSelectRowAtIndexPath:, establecería la propiedad accessoryType de la celda de la vista de tabla dependiendo de su valor actual (para que se active y desactive con múltiples toques). Por ejemplo:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path { 
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:path]; 

    if (cell.accessoryType == UITableViewCellAccessoryCheckmark) { 
     cell.accessoryType = UITableViewCellAccessoryNone; 
    } else { 
     cell.accessoryType = UITableViewCellAccessoryCheckmark; 
    } 
} 

Usted podría mantener una gran variedad de estados seleccionados, además de propio estado tipo de accesorio de las células, o iterar sobre las células en la vista de tabla de consulta para el estado de cada uno de ellos con el fin de leer el filas seleccionadas

+15

Esto no funcionará: indexPath hace referencia a la celda incorrecta cuando se selecciona una celda y se desplaza. Como resultado, verá varios elementos marcados. –

+0

Al igual que en el comentario anterior, esto no funcionará ya que tendrá varios elementos seleccionados mientras se desplaza. – Siddharth

+2

-1 Esta solución tiene un problema, una vez que selecciona ciertas filas y desplazamiento puede ver muchas marcas de verificación para las celdas que aún no ha seleccionado. – raaz

1

La vista de tabla de repetición de alarmas de reloj no es una selección múltiple. Piénselo como una lista de casillas de verificación.

Cuando se selecciona una celda, el tipo de letra y el tipo de accesorio cambian y la selección se desvanece. Cuando se selecciona una celda marcada, el color de la fuente y el tipo de accesorio se cambian hacia atrás y la selección se desvanece.

En su método de delegado didSelectRowAtIndexPath, debe establecer el color del texto y el tipo de accesorio para la celda seleccionada, luego deseleccionar la celda. También registraría el nuevo estado en su modelo de datos. Eso podría ser tan simple como una máscara de bits que representa el estado seleccionado, pero depende de qué datos está mostrando.

En su método cellForRowAtIndexPath: dataSource, configure el color del texto y el tipo de accesorio según su modelo de datos.

La selección múltiple real sería similar. Debe hacer un seguimiento de qué celdas se seleccionan y establecer la celda seleccionada de cada estado a medida que se crea o se muestra. Cuando la vista de tabla informa que se ha seleccionado una celda, alternar el estado de selección en su modelo de datos y establecer el estado seleccionado de la celda en consecuencia.

14

Solo un consejo rápido además de la gran respuesta anterior: para imitar el estilo de Apple desde la aplicación de reloj (haciendo que el color de selección de fila se desvanezca después de marcar/desmarcar la fila), agréguelo al didSelectRowAtIndexPath, después de los condicionales :

[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 

De Apple TableMultiSelect guía.

14
self.tableView.allowsMultipleSelection = YES; 
102

Para la selección múltiple, agregue la siguiente línea en viewDidLoad()

tableView.allowsMultipleSelection = true 

Configure cada cell después desencola (o inicialización) en tableView(_:cellForRowAt:)

let selectedIndexPaths = tableView.indexPathsForSelectedRows 
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath) 
cell.accessoryType = rowIsSelected ? .checkmark : .none 
// cell.accessoryView.hidden = !rowIsSelected // if using a custom image 

actualización de cada célula cuando se selecciona/deselecciona

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
    let cell = tableView.cellForRow(at: indexPath)! 
    cell.accessoryType = .checkmark 
    // cell.accessoryView.hidden = false // if using a custom image 
} 

override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 
    let cell = tableView.cellForRow(at: indexPath)! 
    cell.accessoryType = .none 
    // cell.accessoryView.hidden = true // if using a custom image 
} 

Cuando haya terminado, obtener una matriz de todas las filas seleccionadas

let selectedRows = tableView.indexPathsForSelectedRows 

y obtener los datos seleccionados, donde dataArray mapas a las filas de una vista de tabla con sólo 1 sección

let selectedData = selectedRows?.map { dataArray[$0.row].ID } 
+7

No entiendo por qué esta no es la respuesta aceptada ... si usa una celda personalizada también puede cambiar la apariencia en el método setSelected: animado de su celda según el estado de la celda – keisar

+0

¡Genial! Funciona de maravilla. Mejor respuesta para esta pregunta. ¡Lo aprecio! – sermilion

+3

Para obtener los índices de objetos seleccionados NSArray * selectedCells = [self.tableView indexPathsForSelectedRows]; – iCoder86

18

@BrendanBreg implementation no funcionó para mí. @RaphaelOliveira proporcionó good solution, pero cuando desplaza la tabla hacia abajo, las filas incorrectas se seleccionan (porque UITableView almacena en caché sus celdas). Por lo tanto, he modificado ligeramente solución de Rafael:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; 
    cell.accessoryType = UITableViewCellAccessoryCheckmark; 
} 

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; 
    cell.accessoryType = UITableViewCellAccessoryNone; 
} 

/*Here is modified part*/ 

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    /* 
    ... 
    Your implementation stays here 
    we're just adding few lines to make sure 
    that only correct rows will be selected 
    */ 

    if([[tableView indexPathsForSelectedRows] containsObject:indexPath]) { 
     cell.accessoryType = UITableViewCellAccessoryCheckmark; 
    } else { 
     cell.accessoryType = UITableViewCellAccessoryNone; 
    } 
} 
+0

¡Al desplazarse, se ocultan las filas seleccionadas! – Saqib

1

No se puede teclear off indexPath debido a que las células que se refiere a cambios a medida que Scoll. Ponga un NSLog en cellForRowAtIndexPath para ver eso. Puede marcar/desmarcar en willSelectRowAtIndexPath o didSelectRowAtIndexPath. Sin embargo, eso solo cubre la marca de verificación inicial o desmarcar, y también tendrá los elementos que aparecen marcados una vez que se haya desplazado debido a que la celda subyacente de un indexPath dado cambia.

Así que la solución que encontré es tener una matriz de elementos seleccionados con algo que sea específico para esa celda dada, y hacer la comprobación inicial.

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; 

    if (![selectedIndexes containsObject:cell.textLabel.text]){ 
    [selectedIndexes addObject:cell.textLabel.text]; 
    cell.accessoryType = UITableViewCellAccessoryCheckmark; 
    } else { 
    [selectedIndexes removeObject:cell.textLabel.text]; 
    cell.accessoryType = UITableViewCellAccessoryNone; 
    } 

    return indexPath; 
} 

También tiene que tener la lógica en cellForRowAtIndexPath para asegurarse de que la materia derecha está marcada o no como se desplaza la visualización:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 

    ... 

    cell.accessoryType = UITableViewCellAccessoryNone; 
    if ([selectedIndexes containsObject:cell.textLabel.text]) { 
    cell.accessoryType = UITableViewCellAccessoryCheckmark; 
    [cell setSelected:YES animated:YES]; 
    } 

    return cell; 
} 
+0

-1 indexPath en tableView: didDeselectRowAtIndexPath: NO depende del desplazamiento, y no hay necesidad de mantener una matriz separada de elementos seleccionados. Ver la solución @Dmitry para una implementación correcta – fishinear

3

He visto este problema con tantos desarrolladores. Debido a la naturaleza de la celda de reutilización de la vista de tabla, elimina o realiza al azar los controles. He creado una solución de trabajo para eso. Clonar/descargar código de DevelopmentSupportWorkspace y ejecutar el proyecto UITableViewTest desde allí.

Aquí es veraniega código para que:

@interface CheckBoxTestTableViewController() 

@property (nonatomic, strong) NSArray *dataArray; 
@property (nonatomic, strong) NSMutableDictionary *selectedIndexDictionary; 

@end 

@implementation CheckBoxTestTableViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    // Uncomment the following line to preserve selection between presentations. 
    // self.clearsSelectionOnViewWillAppear = NO; 

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 
    // self.navigationItem.rightBarButtonItem = self.editButtonItem; 

    // 
    _dataArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImageList" ofType:@"plist"]]; 
    _selectedIndexDictionary = [NSMutableDictionary dictionary]; 
} 

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

#pragma mark - Table view data source 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
    return 1; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return _dataArray.count; 
} 


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"checkMarkCell" forIndexPath:indexPath]; 
    cell.accessoryType = UITableViewCellAccessoryNone; 

    // Configure the cell... 
    cell.textLabel.text = _dataArray[indexPath.row][@"text"]; 
    if (_selectedIndexDictionary[indexPath] != nil) cell.accessoryType = UITableViewCellAccessoryCheckmark; 

    return cell; 
} 

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { 
    if (_selectedIndexDictionary[indexPath] == nil) { 
     [_selectedIndexDictionary setObject:_dataArray[indexPath.row] forKey:indexPath]; 
     [[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark]; 
    }else{ 
     [_selectedIndexDictionary removeObjectForKey:indexPath]; 
     [[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryNone]; 
    } 
// [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
} 
@end 
1

1 - Permitir selección múltiple y de conmutación de estado seleccionado:

tableView.allowsMultipleSelection = true 

2 - Recoger o conseguir un arreglo de todos los índices seleccionados cuando se está hecho:

let selected = tableView.indexPathsForSelectedRows 

Nota: Esto es independiente de las celdas que se muestran actualmente en la pantalla.

3 - Cambiar el aspecto de la célula dependiendo del estado seleccionado: reemplazar este método en la subclase UITableViewCell. Si no tiene una subclase, haga una.

// In UITableViewCell subclass 
override func setSelected(selected: Bool, animated: Bool) { 
    super.setSelected(selected, animated: animated) 
    accessoryType = selected ? .Checkmark : .None 
} 
Cuestiones relacionadas