UITableview: Cómo deshabilitar la selección para algunas filas pero no para otras


298

Estoy mostrando en un grupo tableviewcontenidos analizados desde XML. Quiero deshabilitar el evento de clic en él (no debería poder hacer clic en absoluto) La tabla contiene dos grupos. Quiero deshabilitar la selección solo para el primer grupo pero no para el segundo grupo. Al hacer clic en la primera fila del segundo grupo navigatesa mi tubo player view.

¿Cómo puedo hacer que solo grupos o filas específicos sean seleccionables?

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if(indexPath.section!=0)
    if(indexPath.row==0)    

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:tubeUrl]];   
}

Gracias.

Respuestas:


499

Solo tienes que poner este código en cellForRowAtIndexPath

Para deshabilitar la propiedad de selección de la celda: (mientras toca la celda)

cell.selectionStyle = UITableViewCellSelectionStyleNone;

Para permitir poder seleccionar (tocar) la celda: (tocar la celda)

// Default style
cell.selectionStyle = UITableViewCellSelectionStyleBlue;

// Gray style
cell.selectionStyle = UITableViewCellSelectionStyleGray;

Tenga en cuenta que una celda con selectionStyle = UITableViewCellSelectionStyleNone;todavía causará que la UI llame didSelectRowAtIndexPathcuando el usuario la toque. Para evitar esto, haga lo que se sugiere a continuación y establezca.

cell.userInteractionEnabled = NO;

en lugar. También tenga en cuenta que es posible que desee configurar cell.textLabel.enabled = NO;para atenuar el elemento.


1
Este método generará un error si intenta deslizar para eliminar.
Vive el

116
Este es un hack! Haga eso, use la respuesta a continuación: ¡use willSelectRowAtIndexPath con retorno nil!
Peter Stajger

44
El material userInteractionEnabled no es correcto, el gancho willSelectRowAtIndexPath en su lugar como la gente nota.
Jonny

23
En iOS 6.0 y posterior, tableView:shouldHighlightRowAtIndexPath:es un nuevo UITableViewDelegatemétodo que devuelve en BOOLfunción de si se indexPathdebe resaltar o no lo aprobado . Si está compilando para 6.0 y versiones posteriores, le recomiendo esta nueva API.
cbowns

3
si está haciendo esto en Swift y tiene problemas pero terminó aquí, deberá establecer cell!.selectionStyle = .Nonequé fuerza desenvuelve la celda, lo que le permite acceder a sus propiedades.
Marcos

371

Si desea que una fila (o subconjunto de filas) no sea seleccionable , implemente el UITableViewDelegatemétodo -tableView:willSelectRowAtIndexPath:(también mencionado por TechZen). Si indexPathno se puede seleccionar , devuelva nil, de lo contrario devuelva el indexPath. Para obtener el comportamiento de selección predeterminado, simplemente devuelve lo indexPathpasado a su delegatemétodo, pero también puede alterar la selección de fila devolviendo un valor diferente indexPath.

ejemplo:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // rows in section 0 should not be selectable
    if ( indexPath.section == 0 ) return nil;

    // first 3 rows in any section should not be selectable
    if ( indexPath.row <= 2 ) return nil;

    // By default, allow row to be selected
    return indexPath;
}

2
Pequeño detalle: ment <= 2, no = <2. ¡Mala sonrisa! :)
Jonas Byström

23
También me gustaría agregar que esta es la respuesta correcta, pero también debe establecer el selectionStyle en UITableViewCellSelectionStyleNone en cellForRowAtIndexPath. ¿Por qué? porque sin este estilo de selección, la celda no se puede seleccionar, pero aún se mostrará "como seleccionada" cuando se toque (así que cambia al color seleccionado, si hay uno para las otras celdas)
TooManyEduardos

44
esto debería tener más votos a favor que la respuesta aceptada
user1244109

2
Creo que la razón principal por la que esta no es la respuesta aceptada es porque la respuesta aceptada es más fácil. Si bien no es la `` mejor manera '' de hacerlo, su tabla seguirá llamando a setSelected: en sus celdas, todavía funciona visualmente. Esta respuesta aquí requiere más trabajo, pero es la forma correcta de hacerlo (también debe combinar la sugerencia @TooManyEduardos) para evitar cualquier consecuencia imprevista de la tabla que llama a setSelected: en sus celdas.
Brian Sachetta

Me gusta esta respuesta, pero si no desea duplicar su lógica en cellForRowAtIndexPath y willSelectRowAtIndexPath, simplemente verifique si cell.selectionStyle == none en willSelectRowAtIndexPath devolverá cero (vea mi respuesta a continuación)
Eric D'Souza

61

A partir de iOS 6, puedes usar

-tableView:shouldHighlightRowAtIndexPath:

Si regresa NO, deshabilita el resaltado de selección y los segmentos activados por el guión gráfico conectados a esa celda.

Se llama al método cuando se toca un toque en una fila. Volver NOa ese mensaje detiene el proceso de selección y no hace que la fila seleccionada actualmente pierda su aspecto seleccionado mientras el toque está presionado.

Referencia del protocolo UITableViewDelegate


3
Convenido. Para iOS 7 y superior, este debería ser el método preferido (de hecho, en iOS 8, otros métodos no funcionaban para mí)
Race_carr

Esta debería ser la mejor respuesta.
Cameron E

16

Ninguna de las respuestas anteriores realmente aborda el problema correctamente. La razón es que queremos deshabilitar la selección de la celda, pero no necesariamente las subvistas dentro de la celda.

En mi caso, estaba presentando un interruptor UIS en el medio de la fila y quería deshabilitar la selección para el resto de la fila (que está vacía) ¡pero no para el interruptor! La forma correcta de hacerlo es, por lo tanto, en el método

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

donde una declaración de la forma

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];

deshabilita la selección para la celda específica y al mismo tiempo permite al usuario manipular el interruptor y, por lo tanto, usar el selector apropiado. Esto no es cierto si alguien deshabilita la interacción del usuario a través de

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

Método que simplemente prepara la célula y no permite la interacción con el UISwitch.

Además, usando el método

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

para anular la selección de la celda con una declaración del formulario

[tableView deselectRowAtIndexPath:indexPath animated:NO];

todavía muestra la fila que se está seleccionando mientras el usuario presiona la vista de contenido original de la celda.

Solo mis dos centavos. Estoy bastante seguro de que muchos encontrarán esto útil.


De acuerdo. Y regresar nil en willSelectRowAtIndexPathtiene el mismo problema, las subvistas no son seleccionables.
jeffjv

También tenga en cuenta que si tiene una tabla estática, puede establecer la Selección en Ninguno en el Creador de interfaces para las celdas en las que no desea que aparezca la selección.
jeffjv

11

Atrapa las selecciones con estos métodos de origen de datos.

 tableView:willSelectRowAtIndexPath: 
 tableView:didSelectRowAtIndexPath: 
 tableView:willDeselectRowAtIndexPath: 
 tableView:didDeselectRowAtIndexPath:

En estos métodos, verifica si la fila seleccionada es la que desea que se pueda seleccionar. Si es así, tome una acción, si no, no haga nada.

Desafortunadamente, no puede desactivar la selección para una sola sección. Es toda la mesa o nada.

Sin embargo, puede establecer las celdas de la tabla selectionStyle propiedad de en UITableViewCellSelectionStyleNone. Creo que eso hará invisible la selección. Combinado con los métodos anteriores que deberían hacer que las células se vean completamente inertes desde la perspectiva del usuario.

Edit01:

Si tiene una tabla en la que solo algunas de las filas son seleccionables, es importante que las celdas de las filas seleccionables sean visualmente distintas de las filas no seleccionables. El botón accesorio chevron es la forma predeterminada de hacer esto.

Independientemente de cómo lo haga, no quiere que sus usuarios intenten seleccionar filas y piensen que la aplicación ha fallado porque la fila no hace nada.


elabore cómo usar estos métodos o proporcione algunos enlaces de programas de muestra
Warrior

2
@Warrior: si ha instalado el SDK, puede abrir Xcode, vaya a Help -> Developer documentation, luego copie los nombres de los métodos en el campo de búsqueda para ver cómo funciona. La documentación del desarrollador también contiene código de muestra.
kennytm

10

Para Swift 4.0 por ejemplo:

cell.isUserInteractionEnabled = false
cell.contentView.alpha = 0.5

9

Debe hacer algo como lo siguiente para deshabilitar la selección de celdas dentro del cellForRowAtIndexPathmétodo:

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setUserInteractionEnabled:NO];

Para mostrar la celda en gris, coloque lo siguiente dentro del tableView:WillDisplayCell:forRowAtIndexPathmétodo:

[cell setAlpha:0.5];

Un método le permite controlar la interactividad, el otro le permite controlar la apariencia de la interfaz de usuario.


5

Para detener solo algunas celdas seleccionadas, use:

cell.userInteractionEnabled = NO;

Además de evitar la selección, esto también detiene tableView: didSelectRowAtIndexPath: se llama para las celdas que lo tienen configurado.


1
Algo extraño sucede todo el tiempo: cuando configuro userInteractionEnabledNO en una celda (más claramente, un indexPath), la interacción del usuario de algunas OTRAS celdas se desactiva. No tengo idea de por qué sucede esto cada vez que lo uso userInteractionEnableden las celdas de la vista de tabla. ¿Es un error conocido o hice algo mal?
Philip007

3
@ Philip007 ¿Se están reutilizando las células "NO"? Si es así, es posible que deba asegurarse de establecerlo en "SÍ" cuando retire una celda.
JosephH

1
¿Por qué establecerlo en "SÍ"? Quiero que sea NO (no permitir la interacción del usuario). Un ejemplo típico es que configuro la interacción del usuario de la primera fila ( if [indexPath row] == 0) NOen tableView:cellForRowAtIndexPath:después de quitar la celda. Por lo general, funciona bien al principio. Luego, después de varias llamadas de reloadData, de repente, la quinta fila no permite la interacción, luego la cuarta fila ... No puedo entender cuándo sucederá. Todo parece aleatorio.
Philip007

66
@ Philip007 Si está reutilizando celdas, deberá restablecerlo a "SÍ" para las celdas que SI desea que sean interactivas. De lo contrario, si una celda "NO" se reutiliza en una fila que desea ser interactiva, aún estará deshabilitada. es decir:if [indexPath row] == 0) { set to NO } else { set to YES }
JosephH

5

Puede usar el tableView:willDisplayCellmétodo para hacer todos los tipos de personalización de una tableViewCell.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
     [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
     [cell setUserInteractionEnabled:NO];

     if (indexPath.section == 1 && indexPath.row == 0)
     {
         [cell setSelectionStyle:UITableViewCellSelectionStyleGray];
         [cell setUserInteractionEnabled:YES];
     }
} 

En este código anterior, el usuario solo puede seleccionar la primera fila en la segunda sección de tableView. El resto no se pueden seleccionar todas las filas. Gracias ~


5

Para rápido:

cell.selectionStyle = .None
cell.userInteractionEnabled = false

5

Mi solución basada en el modelo de datos:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
    return rowDetails.enabled ? indexPath : nil
}

func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
    let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
    return rowDetails.enabled
}

5

Para swift 4.0 Esto hará el truco. Deshabilitará la celda en el método didSelectRowAtIndexPath pero mantendrá las subvistas haciendo clic.

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
         if (indexPath.row == clickableIndex ) { 
            return indexPath
         }else{
            return nil
        }
    }

O para abreviar: return indexPath.row == clickableIndex ? indexPath : nilmantiene su código un poco más limpio ;-)
Marque el

1

Use esto para hacer que la celda parezca deshabilitada y no seleccionable:

cell.selectionStyle = UITableViewCellSelectionStyleNone;

Importante: tenga en cuenta que esta es solo una propiedad de estilo y que en realidad no deshabilita la celda. Para hacer eso, debe verificar selectionStyleen la didSelectRowAtIndexPath:implementación de su delegado:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if(cell.selectionStyle == UITableViewCellSelectionStyleNone) {
        return;
    }

    // do your cell selection handling here
}

1

Me gusta la respuesta de Brian Chapados arriba. Sin embargo, esto significa que puede tener una lógica duplicada en cellForRowAtIndexPath y luego en willSelectRowAtIndexPath, que puede desincronizarse fácilmente. En lugar de duplicar la lógica, simplemente verifique el selectionStyle:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
        return nil;

    else
        return indexPath;
}

1

Me pareció útil ya que funciona con tablas estáticas y dinámicas. Solo configuro el indicador de divulgación en aquellas celdas que deseo permitir la selección.

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.accessoryType != UITableViewCellAccessoryDisclosureIndicator) {
        return nil;
    }
    return indexPath;
}

1

en Xcode 7 sin codificar, simplemente puede hacer lo siguiente:

En la vista de esquema, seleccione Vista de tabla Celda. (La celda está anidada en Escena del controlador de vista de tabla> Controlador de vista de tabla> Vista de tabla. Puede que tenga que revelar esos objetos para ver la celda de vista de tabla)

En el inspector de atributos, busque el campo etiquetado Selección y seleccione Ninguno. inspector de atributos Con esta opción, la celda no obtendrá un resaltado visual cuando un usuario la toque.


0

SENCILLO

Solo use cell.userInteractionEnabled = YES;la celda si puede navegar y de lo cell.userInteractionEnabled = NO;contrario


1
Esto también evita la interacción con cualquier vista accesoria dentro de la celda, que puede no ser el comportamiento deseado.
Greg Brown

0

Implemente solo el método tableView:willSelectRowAtIndexPath: en la fuente de datos para su tabla. Si desea resaltar la fila en la ruta, devuelva el indexPath dado. Si no lo hace, regrese nulo.

Ejemplo de mi aplicación:

- (NSIndexPath *)tableView:(UITableView *)tableView
    willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MySelectableObj* obj = [self objectAtPath:indexPath];
    if(obj==nil) return nil;
    return indexPath;
}

Lo bueno de esto es que shouldPerformSegueWithIdentifier:sender:no se llamará si el método anterior devuelve nulo, aunque repito la prueba anterior solo para completar.



0

Estoy de acuerdo con la respuesta de Bryan

si lo hago

cell.isUserInteractionEnabled = false 

entonces las subvistas dentro de la celda no serán interactuadas por el usuario.

En el otro sitio, configuración

cell.selectionStyle = .none

activará el método didSelect a pesar de no actualizar el color de selección.

Usar willSelectRowAt es la forma en que resolví mi problema. Ejemplo:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {

    if indexPath.section == 0{

        if indexPath.row == 0{
            return nil
        }

    }
    else if indexPath.section == 1{

        if indexPath.row == 0{
            return nil
        }

    }

    return indexPath

}

0

además de tableView.allowsMultipleSelection = trueque tendrá que anular la selección del resto de las celdas de la sección al seleccionar:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.section does not support multiple selection {
        // deselect the rest of cells
        tableView
            .indexPathsForSelectedRows?.compactMap { $0 }
            .filter { $0.section == indexPath.section && $0.row != indexPath.row }
            .forEach { tableView.deselectRow(at: $0, animated: true) }
    }
}

-1

para swift 3.0 puede usar el siguiente código para deshabilitar la interacción del usuario con la celda UITableView

 cell.isUserInteractionEnabled = false
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.