UITableView establecido en celdas estáticas. ¿Es posible ocultar algunas de las células mediante programación?


132

UITableView establecido en celdas estáticas.

¿Es posible ocultar algunas de las células mediante programación?

Respuestas:


48

Estás buscando esta solución:

StaticDataTableViewController 2.0

https://github.com/xelvenone/StaticDataTableViewController

¡que puede mostrar / ocultar / recargar cualquier celda estática con o sin animación!

[self cell:self.outletToMyStaticCell1 setHidden:hide]; 
[self cell:self.outletToMyStaticCell2 setHidden:hide]; 
[self reloadDataAnimated:YES];

Nota para usar siempre solo (reloadDataAnimated: YES / NO) (no llame [self.tableView reloadData] directamente)

Esto no utiliza la solución hacky con la configuración de altura a 0 y le permite animar el cambio y ocultar secciones enteras


66
Un problema con esa solución es que deja huecos donde estaban las celdas ocultas.
Jack

¿Cómo quieres decir huecos? ¿Puedes describir mejor la situación, tal vez en Github directamente? ¿corriste la muestra?
Peter Lapisu

1
Quiero decir que si tiene, por ejemplo, tres secciones y oculta las celdas de la sección central, el espacio donde estaba esa sección permanece. No probé el proyecto de muestra, pero probé el código. Voy a probar la muestra.
Jack

1
mira la muestra, hubo una actualización más grande, tal vez tenías algún código viejo ... funciona perfecto para mí
Peter Lapisu

Probé la muestra y parece funcionar allí, lo único que estoy haciendo diferente es usar un IBOutletCollectionpero no veo cómo eso marcaría la diferencia. Solo descargué el código ayer, así que no creo que sea una versión antigua.
Jack

164

Para ocultar celdas estáticas en UITable:

  1. Agrega este método :

En su clase delegada del controlador UITableView:

C objetivo:

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

    if(cell == self.cellYouWantToHide)
        return 0; //set the hidden cell's height to 0

    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

Rápido:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    var cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}

Se llamará a este método para cada celda en la UITable. Una vez que lo llama para la celda que desea ocultar, establecemos su altura en 0. Identificamos la celda objetivo creando una salida para ella:

  1. En el diseñador, cree una salida para las celdas que desea ocultar. La salida para una de estas celdas se llama "cellYouWantToHide" arriba.
  2. Marque "Subvistas de clip" en el IB para ver las celdas que desea ocultar. Las celdas que está ocultando deben tener ClipToBounds = YES. De lo contrario, el texto se acumulará en UITableView.

10
Buena solución Para evitar el ClipToBoundsproblema, también puede configurar la celda como oculta. Esto me parecería más limpio. ;-)
MonsieurDart

13
+1 para ClipToBounds = YES. Numerosas soluciones en otros hilos se han perdido eso.
Jeff

2
Gran idea. Esta es la solución más fácil que encontré. Otras soluciones tenían código en dos o más ubicaciones. Por cierto, en IB, ClipToBounds aparece como "Subvistas de Clip".
VaporwareWolf

1
Solo una pequeña corrección, use self .cellYouWantToHide en lugar de este .cellYouWantToHide.
Zoltan Vinkler

2
sin crear un objeto de celda, también podemos usar "index.row" para ocultar el "uitableviewcell"
g212gs

38

La mejor manera es como se describe en el siguiente blog http://ali-reynolds.com/2013/06/29/hide-cells-in-static-table-view/

Diseñe su vista de tabla estática como normal en el generador de interfaces, completa con todas las celdas potencialmente ocultas. Pero hay una cosa que debe hacer por cada celda potencial que desea ocultar: verifique la propiedad "Subvistas de clip" de la celda, de lo contrario el contenido de la celda no desaparecerá cuando intente ocultarla (reduciendo su altura - más tarde).

SO: tiene un interruptor en una celda y se supone que el interruptor oculta y muestra algunas celdas estáticas. Conéctelo a un IBAction y allí haga esto:

[self.tableView beginUpdates];
[self.tableView endUpdates];

Eso te da animaciones agradables para las celdas que aparecen y desaparecen. Ahora implemente el siguiente método de delegado de vista de tabla:

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 1 && indexPath.row == 1) { // This is the cell to hide - change as you need
    // Show or hide cell
        if (self.mySwitch.on) {
            return 44; // Show the cell - adjust the height as you need
        } else {
            return 0; // Hide the cell
        }
   }
   return 44;
}

Y eso es. Mueva el interruptor y la celda se esconde y vuelve a aparecer con una animación agradable y suave.


Actualización: también debe configurar la etiqueta de la celda para que se oculte / muestre con usted Ocultar / mostrar la celda, de lo contrario, las etiquetas harán un desastre.
Mohamed Saleh

1
Debe tener en cuenta que si cualquier UIView dentro del contenido de su celda estática tiene alguna restricción hacia la celda.contenido, puede obtener un error de tiempo de ejecución si esas restricciones no son válidas para su nueva altura de celda.
Pedro Borges

1
La mejor respuesta. Gracias.
Eric Chen

1
Sabía que este truco funcionaba con contenido dinámico. Funciona bastante bien con estática también. Tan simple también.
Hormigas

2
Este realmente funciona, pero para cubrir un punto faltante: si la fila que se ocultará / mostrará contiene un selector de fecha, solo se hace [self.tableView beginUpdates]; [self.tableView endUpdates]; seguirá dejando un problema técnico mientras lo oculta. Para eliminar la falla, debe llamar a [self.tableView reloadRowsAtIndexPaths: @ [indexPathOfTargetRow] withRowAnimation: UITableViewRowAnimationNone]; También para notar que la animación de fila está de alguna manera "deshabilitada" aquí.
Chris

34

Mi solución va en una dirección similar a la de Gareth, aunque hago algunas cosas de manera diferente.

Aquí va:

1. Ocultar las celdas

No hay forma de ocultar directamente las celdas. UITableViewControlleres la fuente de datos que proporciona las celdas estáticas, y actualmente no hay forma de decirle "no proporcione la celda x". Entonces tenemos que proporcionar nuestra propia fuente de datos, que delega enUITableViewController para obtener las celdas estáticas.

Lo más fácil es subclasificar UITableViewControllery anular todos los métodos que deben comportarse de manera diferente al ocultar celdas .

En el caso más simple (tabla de sección única, todas las celdas tienen la misma altura), esto sería así:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section    
{
    return [super tableView:tableView numberOfRowsInSection:section] - numberOfCellsHidden;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Recalculate indexPath based on hidden cells
    indexPath = [self offsetIndexPath:indexPath];

    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (NSIndexPath*)offsetIndexPath:(NSIndexPath*)indexPath
{
    int offsetSection = indexPath.section; // Also offset section if you intend to hide whole sections
    int numberOfCellsHiddenAbove = ... // Calculate how many cells are hidden above the given indexPath.row
    int offsetRow = indexPath.row + numberOfCellsHiddenAbove;

    return [NSIndexPath indexPathForRow:offsetRow inSection:offsetSection];
}

Si su tabla tiene varias secciones, o las celdas tienen diferentes alturas, debe anular más métodos. Aquí se aplica el mismo principio: debe compensar indexPath, section y row antes de delegar a super.

También tenga en cuenta que el parámetro indexPath para métodos como didSelectRowAtIndexPath: será diferente para la misma celda, dependiendo del estado (es decir, el número de celdas ocultas). Por lo tanto, probablemente sea una buena idea compensar siempre cualquier parámetro indexPath y trabajar con estos valores.

2. Animar el cambio

Como ya dijo Gareth, obtienes fallas importantes si animas los cambios usando reloadSections:withRowAnimation: método.

Descubrí que si llamas reloadData: inmediatamente después, la animación mejora mucho (solo quedan pequeños fallos). La tabla se muestra correctamente después de la animación.

Entonces, lo que estoy haciendo es:

- (void)changeState
{
     // Change state so cells are hidden/unhidden
     ...

    // Reload all sections
    NSIndexSet* reloadSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfSectionsInTableView:tableView])];

    [tableView reloadSections:reloadSet withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView reloadData];
}

2
Dios te bendiga, dulce, dulce persona.
guptron

¿Has encontrado una solución para los pequeños fallos de animación? para mí, las líneas del separador entre las celdas no se animarán correctamente y la identificación no usará ninguna animación que una gliculada. ¡gran solución sin embargo!
Maximilian Körner

Mi problema es que con las celdas estáticas numberOfRowsInSection:solo se llama cuando la tabla se carga por primera vez. Cuando llamo a [self.tableView reloadData], numberOfRowsInSection:nunca se vuelve a llamar. Solo cellForRowAtIndexPath:se llama. ¿Qué me estoy perdiendo?
Roman

13
  1. En el diseñador, cree una salida para las celdas que desea ocultar. Por ejemplo, desea ocultar 'cellOne', así que en viewDidLoad () haga esto

cellOneOutlet.hidden = true

ahora anule el método a continuación, verifique qué estado de celda está oculto y devuelva la altura 0 para esas celdas. Esta es una de las muchas formas en que puede ocultar cualquier celda en tableView estático de forma rápida.

override func tableView(tableView: UITableView, heightForRowAtIndexPathindexPath: NSIndexPath) -> CGFloat 
{

let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath)

        if tableViewCell.hidden == true
        {
            return 0
        }
        else{
             return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
        }

}

¡La mejor solución hasta ahora!
derdida

agregue tableView.reloadData () después de hacer cambios y es perfecto. ¡Me ahorró mucho esfuerzo para cambiar la solución! gracias
Shayan C

1
Swift 4 no me deja usar let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath). Supongo que se reemplaza por let tableViewCell = tableView.cellForRow(at: indexPath as IndexPath).
Clifton Labrum

Como estoy usando celdas estáticas en a UITableViewController, no tengo ningún UITableViewmétodo de delegado. Para que llame heightForRow, ¿necesito también otros métodos?
Clifton Labrum

@CliftonLabrum no, solo puede anular este método.
Alexander Ershov el

11

Se me ocurrió una alternativa que en realidad oculta secciones y no las elimina. Intenté el enfoque de @ henning77, pero seguí teniendo problemas cuando cambié el número de secciones del UITableView estático. Este método me ha funcionado muy bien, pero principalmente estoy tratando de ocultar secciones en lugar de filas. Estoy eliminando algunas filas sobre la marcha con éxito, pero es mucho más complicado, por lo que he tratado de agrupar las cosas en secciones que necesito mostrar u ocultar. Aquí hay un ejemplo de cómo estoy ocultando secciones:

Primero declaro una propiedad NSMutableArray

@property (nonatomic, strong) NSMutableArray *hiddenSections;

En viewDidLoad (o después de haber consultado sus datos) puede agregar secciones que desea ocultar a la matriz.

- (void)viewDidLoad
{
    hiddenSections = [NSMutableArray new];

    if(some piece of data is empty){
        // Add index of section that should be hidden
        [self.hiddenSections addObject:[NSNumber numberWithInt:1]];
    }

    ... add as many sections to the array as needed

    [self.tableView reloadData];
}

Luego implemente los siguientes métodos de delegado de TableView

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return nil;
    }

    return [super tableView:tableView titleForHeaderInSection:section];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        return 0;
    }

    return [super tableView:tableView heightForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        [cell setHidden:YES];
    }
}

Luego configure la altura del encabezado y pie de página en 1 para las secciones ocultas porque no puede establecer la altura en 0. Esto provoca un espacio adicional de 2 píxeles, pero podemos compensarlo ajustando la altura del siguiente encabezado visible.

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 
{
    CGFloat height = [super tableView:tableView heightForHeaderInSection:section];

    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        height = 1; // Can't be zero
    }
    else if([self tableView:tableView titleForHeaderInSection:section] == nil){ // Only adjust if title is nil
        // Adjust height for previous hidden sections
        CGFloat adjust = 0;

        for(int i = (section - 1); i >= 0; i--){
            if([self.hiddenSections containsObject:[NSNumber numberWithInt:i]]){
                adjust = adjust + 2;
            }
            else {
                break;
            }
        }

        if(adjust > 0)
        {                
            if(height == -1){
                height = self.tableView.sectionHeaderHeight;
            }

            height = height - adjust;

            if(height < 1){
                height = 1;
            }
        }
    }

    return height;
}

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 
{   
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return 1;
    }
    return [super tableView:tableView heightForFooterInSection:section];
}

Luego, si tiene filas específicas para ocultar, puede ajustar numberOfRowsInSection y qué filas se devuelven en cellForRowAtIndexPath. En este ejemplo, aquí tengo una sección que tiene tres filas donde tres pueden estar vacías y deben eliminarse.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSInteger rows = [super tableView:tableView numberOfRowsInSection:section];

    if(self.organization != nil){
        if(section == 5){ // Contact
            if([self.organization objectForKey:@"Phone"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"Email"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"City"] == [NSNull null]){     
                rows--;
            }
        }
    }

    return rows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{    
    return [super tableView:tableView cellForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

Use este offsetIndexPath para calcular el indexPath para las filas donde está eliminando condicionalmente las filas. No es necesario si solo está ocultando secciones

- (NSIndexPath *)offsetIndexPath:(NSIndexPath*)indexPath
{
    int row = indexPath.row;

    if(self.organization != nil){
        if(indexPath.section == 5){
            // Adjust row to return based on which rows before are hidden
            if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Address"] != [NSNull null]){     
                row = row + 2;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] != [NSNull null] && [self.organization objectForKey:@"Email"] == [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
        }
    }

    NSIndexPath *offsetPath = [NSIndexPath indexPathForRow:row inSection:indexPath.section];

    return offsetPath;
}

Hay muchos métodos para anular, pero lo que me gusta de este enfoque es que es reutilizable. Configure la matriz hiddenSections, agréguela y ocultará las secciones correctas. Ocultando las filas es un poco más complicado, pero posible. No podemos simplemente establecer el alto de las filas que queremos ocultar a 0 si estamos usando un UITableView agrupado porque los bordes no se dibujarán correctamente.


1
Probablemente usaría NSMutableSetpara en su hiddenSectionslugar. Es mucho más rápido ya que en su mayoría está probando la membresía.
pixelfreak

Buena respuesta, Austin. He votado esa respuesta y la usaré como referencia para mis proyectos en el futuro. pixelfreak también hace un buen punto sobre el uso de NSMutableSetfor hiddenSections, aunque entiendo que el punto de su respuesta fue más conceptual que selectivo sobre qué tipo de estructura de datos debe usar.
BigSauce

10

Resulta que puede ocultar y mostrar celdas en un UITableView estático, y con animación. Y no es tan difícil de lograr.

Proyecto de demostración

Video de demostración del proyecto

La esencia:

  1. Use tableView:heightForRowAtIndexPath: para especificar las alturas de las celdas dinámicamente en función de algún estado.
  2. Cuando el estado cambia, las celdas animadas se muestran / ocultan llamando tableView.beginUpdates();tableView.endUpdates()
  3. No llame por tableView.cellForRowAtIndexPath:dentro tableView:heightForRowAtIndexPath:. Use indexPaths en caché para diferenciar las celdas.
  4. No escondas las células. Establezca la propiedad "Subvistas de clip" en Xcode en su lugar.
  5. Use celdas personalizadas (no simples, etc.) para obtener una buena animación oculta. Además, maneje el diseño automático correctamente para el caso cuando la altura de la celda == 0.

Más información en mi blog (idioma ruso)


1
Este punto fue lo importante para mí: "No ocultar las celdas. Establezca la propiedad" Subvistas de clip "en Xcode".
balkoth

8

Sí, definitivamente es posible, aunque estoy luchando con el mismo problema en este momento. Me las arreglé para ocultar las celdas y todo funciona bien, pero actualmente no puedo hacer que la cosa se anime claramente. Esto es lo que he encontrado:

Estoy ocultando filas en función del estado de un interruptor de ENCENDIDO / APAGADO en la primera fila de la primera sección. Si el interruptor está ENCENDIDO, hay 1 fila debajo de él en la misma sección; de lo contrario, hay 2 filas diferentes.

Tengo un selector llamado cuando se cambia el interruptor, y configuro una variable para indicar en qué estado estoy. Luego llamo:

[[self tableView] reloadData];

Anulo la función tableView: willDisplayCell: forRowAtIndexPath: y si se supone que la celda está oculta, hago esto:

[cell setHidden:YES];

Eso oculta la celda y su contenido, pero no elimina el espacio que ocupa.

Para eliminar el espacio, anule la tabla view: heightForRowAtIndexPath: y devuelva 0 para las filas que deberían estar ocultas.

También debe anular tableView: numberOfRowsInSection: y devolver el número de filas en esa sección. Debe hacer algo extraño aquí para que si su tabla es un estilo agrupado, las esquinas redondeadas se produzcan en las celdas correctas. En mi tabla estática hay un conjunto completo de celdas para la sección, por lo que está la primera celda que contiene la opción, luego 1 celda para las opciones de estado ON y 2 celdas más para las opciones de estado OFF, un total de 4 celdas. Cuando la opción está activada, tengo que devolver 4, esto incluye la opción oculta para que la última opción mostrada tenga un cuadro redondeado. Cuando la opción está desactivada, las dos últimas opciones no se muestran, así que devuelvo 2. Todo esto se siente torpe. Lo siento si esto no está muy claro, es difícil de describir. Solo para ilustrar la configuración, esta es la construcción de la sección de tabla en IB:

  • Fila 0: Opción con interruptor ON / OFF
  • Fila 1: aparece cuando la opción está activada
  • Fila 2: aparece cuando la opción está DESACTIVADA
  • Fila 3: aparece cuando la opción está DESACTIVADA

Entonces, cuando la opción está activada, la tabla informa dos filas que son:

  • Fila 0: Opción con interruptor ON / OFF
  • Fila 1: aparece cuando la opción está activada

Cuando la opción está DESACTIVADA, la tabla informa cuatro filas que son:

  • Fila 0: Opción con interruptor ON / OFF
  • Fila 1: aparece cuando la opción está activada
  • Fila 2: aparece cuando la opción está DESACTIVADA
  • Fila 3: aparece cuando la opción está DESACTIVADA

Este enfoque no se siente correcto por varias razones, es tan lejos como he llegado hasta ahora con mi experimentación, así que avíseme si encuentra una mejor manera. Los problemas que he observado hasta ahora son:

  • Se siente mal decirle a la tabla que el número de filas es diferente de lo que presumiblemente está contenido en los datos subyacentes.

  • Parece que no puedo animar el cambio. Intenté usar tableView: reloadSections: withRowAnimation: en lugar de reloadData y los resultados no parecen tener sentido, todavía estoy tratando de que esto funcione. Actualmente, lo que parece suceder es que tableView no actualiza las filas correctas, por lo que permanece oculto y se debe mostrar un vacío debajo de la primera fila. Creo que esto podría estar relacionado con el primer punto sobre los datos subyacentes.

Esperemos que alguien pueda sugerir métodos alternativos o tal vez cómo ampliar con la animación, pero tal vez esto lo ayude a comenzar. Pido disculpas por la falta de hipervínculos a las funciones, las puse pero el filtro de spam las rechazó porque soy un usuario bastante nuevo.


Estoy bastante seguro de que deberías llamar [[self tableView] reloadData];una vez más después de ocultar las celdas
Shmidt

Lamento revivir esto, pero ¿cómo pudiste subclasificar UITableViewController y usarlo con celdas estáticas? Xcode no me deja.
Dany Joumaa

Nessup, no necesitas subclasificar UITableView. En storyboard, elija su TableViewController, luego haga clic en TableView. Ahora puede seleccionar entre celdas dinámicas / estáticas.
Shmidt

1
Encontré la solución de cómo animar / ocultar celdas estáticas. Simplemente llame return [super tableView:tableView cellForRowAtIndexPath:indexPath];a la UITableViewControllersubclase para crear celdas estáticas. Las rutas de índice son estáticas indexadas, no dinámicas ...
k06a

8

Según la respuesta de Justas, pero para Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cell = super.tableView(tableView, cellForRowAt: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAt: indexPath)
}

1
Es posible que necesite una tableView.reloadRows(at:, with:)para actualizar las celdas si cambia la altura mientras la fila ya está visible.
Frost-Lee

5

Bien, después de intentarlo, tengo una respuesta no común. Estoy usando la variable "isHidden" u "hidden" para verificar si esta celda debe estar oculta.

  1. cree un IBOutlet para su controlador de vista. @IBOutlet weak var myCell: UITableViewCell!

  2. Actualice el myCellen su función personalizada, por ejemplo, puede agregarlo en viewDidLoad:

override func viewDidLoad() { super.viewDidLoad() self.myCell.isHidden = true }

  1. en su método de delegado:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cell = super.tableView(tableView, cellForRowAt: indexPath) guard !cell.isHidden else { return 0 } return super.tableView(tableView, heightForRowAt: indexPath) }

Esto reducirá su lógica en el método de delegado, y solo necesita concentrarse en los requisitos de su negocio.


4

Las respuestas anteriores que ocultan / muestran celdas, cambian rowHeight o se meten con restricciones de diseño automático no me funcionaron debido a problemas de diseño automático. El código se volvió intolerable.

Para una tabla estática simple, lo que mejor funcionó para mí fue:

  1. Cree una salida para cada celda en la tabla estática
  2. Cree una matriz solo con las salidas de las celdas para mostrar
  3. Anule cellForRowAtIndexPath para devolver la celda de la matriz
  4. Reemplace numberOfRowsInSection para devolver el recuento de la matriz
  5. Implemente un método para determinar qué celdas deben estar en esa matriz y llame a ese método siempre que sea necesario, y luego vuelva a cargarData.

Aquí hay un ejemplo de mi controlador de vista de tabla:

@IBOutlet weak var titleCell: UITableViewCell!
@IBOutlet weak var nagCell: UITableViewCell!
@IBOutlet weak var categoryCell: UITableViewCell!

var cellsToShow: [UITableViewCell] = []

override func viewDidLoad() {
    super.viewDidLoad()
    determinCellsToShow()
}

func determinCellsToShow() {
    if detail!.duration.type != nil {
        cellsToShow = [titleCell, nagCell, categoryCell]
    }
    else {
        cellsToShow = [titleCell,  categoryCell]
    }
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    return cellsToShow[indexPath.row]
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return cellsToShow.count
}

¡Esto funcionó muy bien, incluso 5 años después! Muchas gracias.
Schrockwell

2020 y esta es la mejor solución para esto. No se necesita hack.
mdonati

3

Método simple compatible con iOS 11 e IB / Storyboard

Para iOS 11, descubrí que una versión modificada de la respuesta de Mohamed Saleh funcionó mejor, con algunas mejoras basadas en la documentación de Apple. Anima muy bien, evita cualquier truco feo o valores codificados, y utiliza alturas de fila ya establecidas en Interface Builder .

El concepto básico es establecer el alto de fila en 0 para cualquier fila oculta. Luego, use tableView.performBatchUpdatespara activar una animación que funcione de manera consistente.

Establecer las alturas de las celdas

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath == indexPathOfHiddenCell {
        if cellIsHidden {
            return 0
        }
    }
    // Calling super will use the height set in your storyboard, avoiding hardcoded values
    return super.tableView(tableView, heightForRowAt: indexPath)
}

Querrás asegurarte cellIsHiddeny indexPathOfHiddenCellestar configurado adecuadamente para tu caso de uso. Para mi código, son propiedades en mi controlador de vista de tabla.

Alternar la celda

En cualquier método que controle la visibilidad (probablemente una acción de botón o didSelectRow), alterna el estado cellIsHidden, dentro de un performBatchUpdatesbloque:

tableView.performBatchUpdates({
                // Use self to capture for block
                self.cellIsHidden = !self.cellIsHidden 
            }, completion: nil)

Apple recomienda performBatchUpdatessobre beginUpdates/endUpdates siempre que sea posible.


1

Encontré una solución para animar las celdas ocultas en la tabla estática.

// Class for wrapping Objective-C block
typedef BOOL (^HidableCellVisibilityFunctor)();
@interface BlockExecutor : NSObject
@property (strong,nonatomic) HidableCellVisibilityFunctor block;
+ (BlockExecutor*)executorWithBlock:(HidableCellVisibilityFunctor)block;
@end
@implementation BlockExecutor
@synthesize block = _block;
+ (BlockExecutor*)executorWithBlock:(HidableCellVisibilityFunctor)block
{
    BlockExecutor * executor = [[BlockExecutor alloc] init];
    executor.block = block;
    return executor;
}
@end

Solo se necesita un diccionario adicional:

@interface MyTableViewController ()
@property (nonatomic) NSMutableDictionary * hidableCellsDict;
@property (weak, nonatomic) IBOutlet UISwitch * birthdaySwitch;
@end

Y observe la implementación de MyTableViewController. Necesitamos dos métodos para convertir indexPath entre índices visibles e invisibles ...

- (NSIndexPath*)recoverIndexPath:(NSIndexPath *)indexPath
{
    int rowDelta = 0;
    for (NSIndexPath * ip in [[self.hidableCellsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
    {
        BlockExecutor * executor = [self.hidableCellsDict objectForKey:ip];
        if (ip.section == indexPath.section
            && ip.row <= indexPath.row + rowDelta
            && !executor.block())
        {
            rowDelta++;
        }
    }
    return [NSIndexPath indexPathForRow:indexPath.row+rowDelta inSection:indexPath.section];
}

- (NSIndexPath*)mapToNewIndexPath:(NSIndexPath *)indexPath
{
    int rowDelta = 0;
    for (NSIndexPath * ip in [[self.hidableCellsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
    {
        BlockExecutor * executor = [self.hidableCellsDict objectForKey:ip];
        if (ip.section == indexPath.section
            && ip.row < indexPath.row - rowDelta
            && !executor.block())
        {
            rowDelta++;
        }
    }
    return [NSIndexPath indexPathForRow:indexPath.row-rowDelta inSection:indexPath.section];
}

Un cambio de valor de IBAction en UISwitch:

- (IBAction)birthdaySwitchChanged:(id)sender
{
    NSIndexPath * indexPath = [self mapToNewIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]];
    if (self.birthdaySwitch.on)
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    else
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

Algunos métodos UITableViewDataSource y UITableViewDelegate:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int numberOfRows = [super tableView:tableView numberOfRowsInSection:section];
    for (NSIndexPath * indexPath in [self.hidableCellsDict allKeys])
        if (indexPath.section == section)
        {
            BlockExecutor * executor = [self.hidableCellsDict objectForKey:indexPath];
            numberOfRows -= (executor.block()?0:1);
        }
    return numberOfRows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    indexPath = [self recoverIndexPath:indexPath];
    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    indexPath = [self recoverIndexPath:indexPath];
    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // initializing dictionary
    self.hidableCellsDict = [NSMutableDictionary dictionary];
    [self.hidableCellsDict setObject:[BlockExecutor executorWithBlock:^(){return self.birthdaySwitch.on;}] forKey:[NSIndexPath indexPathForRow:1 inSection:1]];
}

- (void)viewDidUnload
{
    [self setBirthdaySwitch:nil];
    [super viewDidUnload];
}

@end

1

Responda rápidamente :

Agregue el siguiente método en su TableViewController:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return indexPathOfCellYouWantToHide == indexPath ? 0 : super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}

si tableView intenta dibujar la celda que desea ocultar, no la mostrará porque su altura se establecerá en 0pt gracias al método anterior, todo lo demás permanece inalterado.

Tenga en cuenta que indexPathOfCellYouWantToHidese puede cambiar en cualquier momento :)


1

En> Swift 2.2, he combinado algunas respuestas aquí.

Haga una salida del guión gráfico para vincular a su staticCell.

@IBOutlet weak var updateStaticCell: UITableViewCell!

override func viewDidLoad() {
    ...
    updateStaticCell.hidden = true
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if indexPath.row == 0 {
        return 0
    } else {
        return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
    }
}

Quiero ocultar mi primera celda, así que configuré la altura en 0 como se describió anteriormente.


1

Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    var height = super.tableView(tableView, heightForRowAt: indexPath)
    if (indexPath.row == HIDDENROW) {
        height = 0.0
    }
    return height
}

0

Para el escenario más fácil cuando oculta celdas en la parte inferior de la vista de tabla, puede ajustar el contenido de TableView después de ocultar la celda:

- (void)adjustBottomInsetForHiddenSections:(NSInteger)numberOfHiddenSections
{
    CGFloat bottomInset = numberOfHiddenSections * 44.0; // or any other 'magic number
    self.tableView.contentInset = UIEdgeInsetsMake(self.tableView.contentInset.top, self.tableView.contentInset.left, -bottomInset, self.tableView.contentInset.right);
}


0

La solución de k06a ( https://github.com/k06a/ABStaticTableViewController ) es mejor porque oculta toda la sección, incluidos los encabezados y pies de página de las celdas, donde esta solución ( https://github.com/peterpaulis/StaticDataTableViewController ) oculta todo excepto el pie de página.

EDITAR

Acabo de encontrar una solución si quieres ocultar el pie de página StaticDataTableViewController. Esto es lo que necesita copiar en el archivo StaticTableViewController.m:

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    } else {
        return [super tableView:tableView titleForFooterInSection:section];
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {

    CGFloat height = [super tableView:tableView heightForFooterInSection:section];

    if (self.originalTable == nil) {
        return height;
    }

    if (!self.hideSectionsWithHiddenRows) {
        return height;
    }

    OriginalSection * os = self.originalTable.sections[section];
    if ([os numberOfVissibleRows] == 0) {
       //return 0;
        return CGFLOAT_MIN;
    } else {
        return height;
    }

    //return 0;
    return CGFLOAT_MIN;
}

0

Seguramente puedes. Primero, regrese al número de celdas tableView que desea mostrar, luego llame superpara lograr cierta celda de su guión gráfico y devuélvala para tableView:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.mode.numberOfCells()
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = super.tableView(tableView, cellForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))

    return cell
}

Si sus celdas tienen una altura diferente, devuélvala también:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return super.tableView(tableView, heightForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))
}

0

Además de la solución @Saleh Masum:

Si obtiene errores de diseño automático , simplemente puede eliminar las restricciones deltableViewCell.contentView

Swift 3:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let tableViewCell = super.tableView(tableView, cellForRowAt: indexPath)

    if tableViewCell.isHidden == true
    {
        tableViewCell.contentView.removeConstraints(tableViewCell.contentView.constraints)
        return 0
    }
    else{
        return super.tableView(tableView, heightForRowAt: indexPath)
    }

}

Esta solución depende del flujo de su aplicación . Si desea mostrar / ocultar la celda en la misma instancia del controlador de vista, esta puede no ser la mejor opción, ya que elimina las restricciones .


0

Obtuve una mejor manera de ocultar celdas estáticas e incluso secciones dinámicamente sin ningún truco.

Establecer el alto de fila en 0 puede ocultar una fila, pero eso no funciona si desea ocultar una sección completa que contendrá algunos espacios incluso si oculta todas las filas.

Mi enfoque es construir una matriz de secciones de celdas estáticas. Luego, el contenido de la vista de tabla será controlado por la matriz de secciones.

Aquí hay un código de muestra:

var tableSections = [[UITableViewCell]]()

private func configTableSections() {
    // seciton A
    tableSections.append([self.cell1InSectionA, self.cell2InSectionA])

    // section B
    if shouldShowSectionB {
        tableSections.append([self.cell1InSectionB, self.cell2InSectionB])
    }

    // section C
    if shouldShowCell1InSectionC {
        tableSections.append([self.cell1InSectionC, self.cell2InSectionC, self.cell3InSectionC])
    } else {
        tableSections.append([self.cell2InSectionC, self.cell3InSectionC])
    }
}

func numberOfSections(in tableView: UITableView) -> Int {
    return tableSections.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tableSections[section].count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return tableSections[indexPath.section][indexPath.row]
}

De esta manera, puede armar todo su código de configuración sin tener que escribir el código desagradable para calcular el número de filas y secciones. Y por supuesto no0 alturas.

Este código también es muy fácil de mantener. Por ejemplo, si desea agregar / eliminar más celdas o secciones.

Del mismo modo, puede crear una matriz de título de encabezado de sección y una matriz de título de pie de página de sección para configurar sus títulos de sección dinámicamente.

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.