¿Cómo cambiar el tamaño de un tableHeaderView de un UITableView?

103

Tengo problemas para cambiar el tamaño de tableHeaderView. Es simple no funciona.

1) Cree un UITableView y UIView (100 x 320 px);

2) Establezca UIView como tableHeaderView de UITableView;

3) Construye y listo. Todo está bien.

Ahora, quiero cambiar el tamaño de tableHeaderView, así que agrego este código en viewDidLoad:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

La altura de tableHeaderView debería aparecer con 200, pero aparece con 100.

Si escribo:

self.tableView.autoresizesSubviews = YES;


CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;


self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

Luego comienza con 200 de altura, como quiero. Pero quiero poder modificarlo en tiempo de ejecución.

También probé esto, sin éxito:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

[self.tableView.tableHeaderView setNeedsLayout];
[self.tableView.tableHeaderView setNeedsDisplay];
[self.tableView setNeedsLayout];
[self.tableView setNeedsDisplay];

El punto aquí es: ¿Cómo cambiamos el tamaño de un tableHeaderView en tiempo de ejecución?

¿Alguien ha podido hacer esto?

Gracias

yo me

yo me
fuente

Respuestas:

180

FYI: Logré que esto funcionara modificando tableHeaderView y volviéndolo a configurar. En este caso, estoy ajustando el tamaño de tableHeaderView cuando la subvista UIWebView ha terminado de cargarse.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];
kubi
fuente
13
+1 Esto funcionó para mí. Llamar a 'setTableHeaderView' después de que su subvista haya cambiado de tamaño es la clave. El problema es que mi subvista cambia de tamaño en un segundo como una animación. Ahora estoy tratando de averiguar cómo animar el tableHeaderView con él.
Andrew
1
Perfecto, muchas gracias. Para mí, este es un ejemplo de una de las cualidades menos deseables de las propiedades en Objective-C. No hay forma de que sepamos (y no hay razón por la que debamos saber) que configurar el encabezado tiene el efecto secundario de volver a calcular la altura. Debería hacerlo automáticamente cuando actualizamos la altura del encabezado, o deberíamos tener que llamar a algo así [tableView recalculateHeaderHeight]cada vez.
jakeboxer
48
@ Andrew Sé que esto es casi un año demasiado tarde, pero es mejor tarde que nunca: pude animar la altura cambiante de la vista del encabezado de la tabla al setTableHeaderView:[tableview beginUpdates][tableview endUpdates]
cerrar
2
@jasongregori comentario útil que agregaste: veo que la misma técnica (beginUpdates + endUpdates) no anima el cambio de altura para un tableFooterView de la forma en que lo hace con un tableHeaderView. ¿Ha descubierto una buena manera de animar la tableFooterView también?
kris
1
Impresionante, este evento funciona cuando se hace dentro de un bloque de animación UIView, aunque no creo que sea muy eficiente en ese caso.
Can
12

Esta respuesta es antigua y aparentemente no funciona en iOS 7 y superior.

Me encontré con el mismo problema y también quería que los cambios se animaran, así que hice una subclase de UIView para mi vista de encabezado y agregué estos métodos:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight{
    NSUInteger oldHeight = self.frame.size.height;
    NSInteger originChange = oldHeight - newHeight;

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:1.0f];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x, 
                        self.frame.origin.y, 
                        self.frame.size.width, 
                        newHeight);

    for (UIView *view in [(UITableView *)self.superview subviews]) {
        if ([view isKindOfClass:[self class]]) {
            continue;
        }
        view.frame = CGRectMake(view.frame.origin.x, 
                            view.frame.origin.y - originChange, 
                            view.frame.size.width, 
                            view.frame.size.height);
    }

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}

Esto esencialmente anima todas las subvistas de UITableView que no son del mismo tipo de clase que la clase que llama. Al final de la animación, llama a setTableHeaderView en la supervista (el UITableView); sin esto, el contenido de UITableView retrocederá la próxima vez que el usuario se desplace. La única limitación que he encontrado en esto hasta ahora es que si el usuario intenta desplazarse por UITableView mientras se realiza la animación, el desplazamiento se animará como si la vista del encabezado no hubiera cambiado de tamaño (no es un gran problema si la animación es rápido).

Garrettmoon
fuente
funciona perfectamente, eliminé la animación porque no la necesitaba.
Jiho Kang
Funcionó perfecto hasta que sucedió iOS7 ... agregué mi solución a continuación
avishic
1
WTF? Estás jugando tanto con los aspectos internos de UITableView que realmente no debería sorprenderte que no funcione en las versiones más recientes de iOS ...;)
Daniel Rinser
10

Si desea animar condicionalmente los cambios, puede hacer lo siguiente:

- (void) showHeader:(BOOL)show animated:(BOOL)animated{

    CGRect closedFrame = CGRectMake(0, 0, self.view.frame.size.width, 0);
    CGRect newFrame = show?self.initialFrame:closedFrame;

    if(animated){
        // The UIView animation block handles the animation of our header view
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        // beginUpdates and endUpdates trigger the animation of our cells
        [self.tableView beginUpdates];
    }

    self.headerView.frame = newFrame;
    [self.tableView setTableHeaderView:self.headerView];

    if(animated){
        [self.tableView endUpdates];
        [UIView commitAnimations];
    }
}

Tenga en cuenta que la animación se divide en dos partes:

  1. La animación de las celdas debajo del tableHeaderView. Esto se hace usando beginUpdatesyendUpdates
  2. La animación de la vista de encabezado real. Esto se hace usando un UIViewbloque de animación.

Para sincronizar esas dos animaciones, animationCurvese debe configurar UIViewAnimationCurveEaseInOuty la duración 0.3, que parece ser lo que UITableView usa para su animación.

Actualizar

Creé un proyecto de Xcode en gihub, que hace esto. Consulta el proyecto ResizeTableHeaderViewAnimateden besi / ios-quickies

captura de pantalla

Besi
fuente
2
Esta técnica funciona, pero no para vistas de tabla con encabezados de sección. Cuando usa las actualizaciones de inicio / finalización, interfiere con el bloque de animación, dejando encabezados de sección duplicados.
BlueFish
9

Creo que debería funcionar si establece la altura de myHeaderView así:

CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;

self.tableView.tableHeaderView = myHeaderView;
Greg Martin
fuente
Esto realmente funciona, pero solo si se usa en viewDidLayoutSubviews
KoCMoHaBTa
6

Usé la solución @garrettmoon anterior hasta iOS 7.
Aquí hay una solución actualizada basada en @ garrettmoon:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight animated:(BOOL)animated {

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:[CATransaction animationDuration]];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x,
                        self.frame.origin.y,
                        self.frame.size.width,
                        newHeight);

    [(UITableView *)self.superview setTableHeaderView:self];

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}
avishic
fuente
5

Esto funcionó para mí en iOS 7 y 8. Este código se ejecuta en el controlador de vista de tabla.

[UIView animateWithDuration:0.3 animations:^{
    CGRect oldFrame = self.headerView.frame;
    self.headerView.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newHeight);
    [self.tableView setTableHeaderView:self.headerView];
}];
Darcy Rayner
fuente
4

Es porque el creador de tableHeaderView.

Debe establecer la altura de UIView antes de establecer tableHeaderView. (Sería mucho más fácil si Apple abriera fuentes de este marco ...)

Thomas Decaux
fuente
4

En iOS 9 y versiones anteriores, tableHeaderViewno se volvería a diseñar después de cambiar su tamaño. Este problema se resuelve en iOS 10.

Para solucionar este problema, hazlo con el siguiente código:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;
klaudz
fuente
2

En iOS 9.x, hacer esto viewDidLoadfunciona bien:

var frame = headerView.frame
frame.size.height = 11  // New size
headerView.frame = frame

headerViewse declara como @IBOutlet var headerView: UIView!y está conectado en el guión gráfico, donde se coloca en la parte superior de tableView, para funcionar como tableHeaderView.

Eneko Alonso
fuente
2

Esto es solo para cuando usa el diseño automático y lo establece translatesAutoresizingMaskIntoConstraints = falseen una vista de encabezado personalizada.

La mejor y más sencilla forma es anular intrinsicContentSize. Se UITableViewutiliza internamente intrinsicContentSizepara decidir el tamaño de su encabezado / pie de página. Una vez que haya anulado intrinsicContentSizeen su vista personalizada, lo que debe hacer es lo siguiente

  1. configurar el diseño de la vista personalizada de encabezado / pie de página (subvistas)
  2. invocar invalidateIntrinsicContentSize()
  3. invocar tableView.setNeedsLayout()ytableView.layoutIfNeeded()

Luego, el UITableViewencabezado / pie de página se actualizará como desee. No es necesario configurar la vista nula o restablecer.

Una cosa realmente interesante para el UITableView.tableHeaderViewo .tableFooterViewes que UIStackViewpierde su capacidad para gestionarlo arrangedSubviews. Si desea usar UIStackViewcomo un tableHeaderView o tableFooterView, debe incrustar el stackView en UIViewy anular UIViewel intrinsicContentSize.

Ryan
fuente
2

Para código Swift 5 probado

override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()

        guard let headerView = self.tblProfile.tableHeaderView else {
            return
        }

        let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

        if headerView.frame.size.height != size.height {
            headerView.frame.size.height = size.height
            self.tblProfile.tableHeaderView = headerView
            self.tblProfile.layoutIfNeeded()
        }
    }

Nota: Debe proporcionar todas las restricciones de la subvista desde arriba, abajo, al principio, al final. Entonces obtendrá el tamaño requerido completo.

Referencia tomada de: https://useyourloaf.com/blog/variable-height-table-view-header/

Hardik Thakkar
fuente
1

Ajuste de la altura de la vista de encabezamiento propiedad tableView.tableHeaderViewen viewDidLoadno parece trabajo, la opinión de la altura del cabezal aún no cambia como se esperaba.

Después de luchar contra este problema durante muchos intentos. Descubrí que puede cambiar la altura invocando la lógica de creación de la vista del encabezado dentro del - (void)didMoveToParentViewController:(UIViewController *)parentmétodo.

Entonces, el código de ejemplo se vería así:

- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];

    if ( _tableView.tableHeaderView == nil ) {
        UIView *header = [[[UINib nibWithNibName:@"your header view" bundle:nil] instantiateWithOwner:self options:nil] firstObject];

        header.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), HeaderViewHeight);

        [_tableView setTableHeaderView:header];
    }
}
Enix
fuente
0

Encontré que el inicializador initWithFrame de un UIView no respeta correctamente el rect que paso. Por lo tanto, hice lo siguiente que funcionó perfectamente:

 - (id)initWithFrame:(CGRect)aRect {

    CGRect frame = [[UIScreen mainScreen] applicationFrame];

    if ((self = [super initWithFrame:CGRectZero])) {

        // Ugly initialization behavior - initWithFrame will not properly honor the frame we pass
        self.frame = CGRectMake(0, 0, frame.size.width, 200);

        // ...
    }
}

La ventaja de esto es que está mejor encapsulado en su código de vista.

Harald Schubert
fuente
Obtener el marco de UIScreenes una mala idea, ¿cómo reutilizarás tu vista?
Zorayr
0

Implementé un cambio de altura animado del encabezado de la tabla para expandirlo a la pantalla general cuando se toca. Sin embargo, el código puede ayudar en otros casos:

// Swift
@IBAction func tapped(sender: UITapGestureRecognizer) {

    self.tableView.beginUpdates()       // Required to update cells. 

    // Collapse table header to original height
    if isHeaderExpandedToFullScreen {   

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = 110   // original height in my case is 110
        })

    }
    // Expand table header to overall screen            
    else {      
        let screenSize = self.view.frame           // "screen" size

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = screenSize.height
        })
    }

    self.tableView.endUpdates()  // Required to update cells. 

    isHeaderExpandedToFullScreen= !isHeaderExpandedToFullScreen  // Toggle
}
Alexander Volkov
fuente
0

Encabezado de cambio de tamaño de UITableView - UISearchBar con barra de alcance

Quería un UITableViewcon a UISearchBarcomo encabezado de la tabla, así que tengo una jerarquía que se ve así

UITableView
  |
  |--> UIView
  |     |--> UISearchBar
  |
  |--> UITableViewCells

Métodos UISearchBarDelegate

Como se ha dicho en otra parte, si no estableceTableViewHeader después de cambiarlo, no sucederá nada.

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = YES;
    [UIView animateWithDuration:0.2f animations:^{
        [searchBar sizeToFit];
        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:YES animated:YES];
    return YES;
}

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = NO;
    [UIView animateWithDuration:0.f animations:^{
        [searchBar sizeToFit];

        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:NO animated:YES];
    return YES;
}
Cameron Lowell Palmer
fuente
0

Si el headerView personalizado está diseñado usando autolayout y headerView debe actualizarse después de la búsqueda web o una tarea perezosa similar. luego en iOS-Swift hice esto y actualicé mi headerView usando el código de abajo:

//to reload your cell data
self.tableView.reloadData()
dispatch_async(dispatch_get_main_queue(),{
// this is needed to update a specific tableview's headerview layout on main queue otherwise it's won't update perfectly cause reloaddata() is called
  self.tableView.beginUpdates()
  self.tableView.endUpdates()
} 
Rafat touqir Rafsun
fuente
0

Obviamente, a estas alturas Apple debería haber implementado UITableViewAutomaticDimension para tableHeaderView y tableFooterView ...

Lo siguiente parece funcionar para mí usando restricciones de diseño:

CGSize   s  = [ self  systemLayoutSizeFittingSize : UILayoutFittingCompressedSize ];
CGRect   f  = [ self  frame ];

f.size   = s;

[ self  setFrame : f ];
demonio digital
fuente
0

Si su tableHeaderView es un webView de contenido ajustable, puede probar:

[self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    self.webView.height = self.webView.scrollView.contentSize.height;
    self.tableView.tableHeaderView = self.webView;
}

Lo probé en iOS9 y iOS11, funcionó bien.

无 夜 之 星辰
fuente
-3

¿Lo intentaste [self.tableView reloadData]después de cambiar la altura?

codelogic
fuente
1
Se trata de tableHeaderView, que es una vista estática. - [UITableView reloadData]solo está destinado a las vistas dinámicas (celdas) y también a los encabezados de sección que obviamente pensaste que estaban destinados;)
Julian F.Weinert