Come ridimensionare un tableHeaderView di un UITableView?


103

Ho problemi a ridimensionare un tableHeaderView. Semplicemente non funziona.

1) Crea un UITableView e UIView (100 x 320 px);

2) Impostare l'UIView come tableHeaderView di UITableView;

3) Costruisci e vai. Va tutto bene.

Ora, voglio ridimensionare tableHeaderView, quindi aggiungo questo codice in 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;

L'altezza del tableHeaderView dovrebbe apparire con 200, ma appare con 100.

Se scrivo:

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;

Quindi inizia con 200 di altezza, come voglio. Ma voglio essere in grado di modificarlo in runtime.

Ho provato anche questo, senza successo:

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];

Il punto qui è: come ridimensioniamo un tableHeaderView in runtime ???

Qualcuno è in grado di farlo?

Grazie

iMe

Risposte:


180

FYI: ho ottenuto questo per funzionare modificando il tableHeaderView e reimpostandolo. In questo caso, sto regolando la dimensione del tableHeaderView quando la sottoview UIWebView ha terminato il caricamento.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];

13
+1 Questo ha funzionato per me. La chiave è chiamare 'setTableHeaderView' dopo che la tua sottoview ha cambiato dimensione. Il problema è che la mia sottoview cambia dimensione in un secondo come animazione. Ora sto cercando di capire come animare tableHeaderView con esso.
Andrew

1
Perfetto, grazie mille. Per me, questo è un esempio di una delle qualità meno desiderabili delle proprietà in Objective-C. Non c'è modo per noi di sapere (e nessun motivo dovremmo sapere) che l'impostazione dell'intestazione ha l'effetto collaterale di ricalcolare l'altezza. Dovrebbe farlo automaticamente quando aggiorniamo l'altezza dell'intestazione, o dovremmo essere tenuti a chiamare qualcosa di simile [tableView recalculateHeaderHeight]ogni volta.
jakeboxer

48
@Andrew So che è in ritardo di quasi un anno, ma meglio tardi che mai: sono stato in grado di animare l'altezza variabile della visualizzazione dell'intestazione del tavolo avvolgendo la setTableHeaderView:chiamata con [tableview beginUpdates]e[tableview endUpdates]
jasongregori

2
@jasongregori pratico commento che hai aggiunto - vedo che la stessa tecnica (beginUpdates + endUpdates) non anima il cambio di altezza per un tableFooterView come fa un tableHeaderView. Hai trovato un buon modo per animare anche tableFooterView?
kris

1
Impressionante, l'evento funziona quando lo si fa all'interno di un blocco di animazione UIView, anche se non credo sia molto efficiente in quel caso.
Può

12

Questa risposta è vecchia e apparentemente non funziona su iOS 7 e versioni successive.

Ho riscontrato lo stesso problema e volevo anche che le modifiche si animassero, quindi ho creato una sottoclasse di UIView per la mia vista dell'intestazione e ho aggiunto questi metodi:

- (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];
}

Questo essenzialmente anima tutte le sottoview di UITableView che non sono dello stesso tipo di classe della classe chiamante. Alla fine dell'animazione, chiama setTableHeaderView sulla superview (l'UITableView) - senza questo il contenuto di UITableView tornerà indietro la prossima volta che l'utente scorre. L'unica limitazione che ho trovato finora è che se l'utente tenta di scorrere l'UITableView mentre è in corso l'animazione, lo scorrimento si animerà come se la vista dell'intestazione non fosse stata ridimensionata (non è un grosso problema se l'animazione è Presto).


funziona perfettamente, ho rimosso l'animazione perché non ne avevo bisogno.
Jiho Kang

Ha funzionato perfettamente fino a quando iOS7 non è successo ... ho aggiunto la mia soluzione di seguito
avishic

1
WTF? Stai scherzando così tanto con gli interni di UITableView che non dovresti davvero essere sorpreso dal fatto che non funzioni nelle versioni iOS più recenti ...;)
Daniel Rinser

10

Se desideri animare in modo condizionale le modifiche, puoi procedere come segue:

- (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];
    }
}

Tieni presente che l'animazione è duplice:

  1. L'animazione delle celle sotto il file tableHeaderView. Questo viene fatto usando beginUpdateseendUpdates
  2. L'animazione della visualizzazione dell'intestazione effettiva. Questo viene fatto utilizzando un UIViewblocco di animazione.

Per sincronizzare queste due animazioni, animationCurvedeve essere impostato su UIViewAnimationCurveEaseInOute la durata su 0.3, che sembra essere ciò che UITableView utilizza per la sua animazione.

Aggiornare

Ho creato un progetto Xcode su gihub, che fa questo. Guarda il progetto ResizeTableHeaderViewAnimatedin besi / ios-quickies

immagine dello schermo


2
Questa tecnica funziona, ma non funziona per le viste tabella con intestazioni di sezione. Quando si utilizza inizio aggiornamenti / fine aggiornamento, interferisce con il blocco dell'animazione, lasciando intestazioni di sezione duplicate.
BlueFish

9

Penso che dovrebbe funzionare se imposti l'altezza di myHeaderView in questo modo:

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

self.tableView.tableHeaderView = myHeaderView;

Funziona effettivamente, ma solo se utilizzato in viewDidLayoutSubviews
KoCMoHaBTa

6

Ho utilizzato la soluzione @garrettmoon sopra fino a iOS 7.
Ecco una soluzione aggiornata basata su @ 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];
}

5

Questo ha funzionato per me su iOS 7 e 8. Questo codice è in esecuzione sul controller della vista tabella.

[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];
}];

4

È perché il setter di tableHeaderView.

Devi impostare l'altezza di UIView prima di impostare tableHeaderView. (Sarebbe molto più semplice se Apple aprisse questo framework ...)


4

Su iOS 9 e versioni precedenti, tableHeaderViewnon re-layout dopo averlo ridimensionato. Questo problema è stato risolto in iOS 10.

Per risolvere questo problema, fallo semplicemente con il seguente codice:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;

2

Su iOS 9.x, questa operazione viewDidLoadfunziona perfettamente:

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

headerViewè dichiarato come @IBOutlet var headerView: UIView!e connesso sullo storyboard, dove è posizionato nella parte superiore di tableView, per funzionare come tableHeaderView.


2

Questo è solo per quando si utilizza il layout automatico e si imposta translatesAutoresizingMaskIntoConstraints = falseuna visualizzazione dell'intestazione personalizzata.

Il modo migliore e più semplice è sovrascrivere intrinsicContentSize. UITableViewUtilizza internamente intrinsicContentSizeper decidere la sua dimensione di intestazione / piè di pagina. Dopo aver eseguito l'override intrinsicContentSizenella visualizzazione personalizzata, ciò che devi fare è come di seguito

  1. configurare il layout della visualizzazione dell'intestazione / piè di pagina personalizzata (visualizzazioni secondarie)
  2. invocare invalidateIntrinsicContentSize()
  3. invocare tableView.setNeedsLayout()etableView.layoutIfNeeded()

Quindi l UITableView'intestazione / piè di pagina di verrà aggiornato come desideri. Non è necessario impostare la visualizzazione su zero o ripristinare.

Una cosa davvero interessante per UITableView.tableHeaderViewo .tableFooterViewè che UIStackViewperde la sua capacità di gestirla arrangedSubviews. Se vuoi usare UIStackViewcome tableHeaderView o tableFooterView, devi incorporare lo stackView in a UIViewe sovrascrivere UIViewil file intrinsicContentSize.


2

Per swift 5 codice testato

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: è necessario assegnare a tutti i vincoli della sottoview i formati top, bottom, leading, trailing. Quindi otterrà l'intera dimensione richiesta.

Riferimento tratto da: https://useyourloaf.com/blog/variable-height-table-view-header/


1

L'impostazione dell'altezza per la proprietà della vista dell'intestazione tableView.tableHeaderViewin viewDidLoadsembra non funzionare, l'altezza della vista dell'intestazione continua a non cambiare come previsto.

Dopo aver combattuto contro questo problema per molti tentativi. Ho scoperto che puoi modificare l'altezza invocando la visualizzazione dell'intestazione per creare la logica all'interno del - (void)didMoveToParentViewController:(UIViewController *)parentmetodo.

Quindi il codice di esempio sarebbe simile a questo:

- (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];
    }
}

0

Ho scoperto che l'inizializzatore initWithFrame di un UIView non rispetta correttamente il rect che passo. Quindi, ho fatto quanto segue che ha funzionato perfettamente:

 - (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);

        // ...
    }
}

Il vantaggio di questo è che è meglio incapsulato nel codice di visualizzazione.


Recuperare la cornice da UIScreenè una cattiva idea, come riutilizzerai la tua vista?
Zorayr

0

Ho implementato la modifica dell'altezza animata dell'intestazione della tabella per espandere lo schermo generale quando viene toccato. Tuttavia, il codice può aiutare in altri casi:

// 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
}

0

Intestazione di ridimensionamento UITableView - UISearchBar con barra di ambito

Volevo un UITableViewcon UISearchBarcome intestazione alla tabella, quindi ho una gerarchia simile a questa

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

Metodi UISearchBarDelegate

Come è stato affermato altrove, se non si impostaTableViewHeader dopo averlo modificato, non accadrà nulla.

- (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;
}

0

Se headerView personalizzato è progettato utilizzando il layout automatico e headerView deve essere aggiornato dopo il web-fetch o un'attività lenta simile. poi in iOS-Swift ho fatto questo e ho aggiornato il mio headerView utilizzando il codice seguente:

//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()
} 

0

Ovviamente, ormai Apple dovrebbe aver implementato UITableViewAutomaticDimension per tableHeaderView e tableFooterView ...

Quanto segue sembra funzionare per me usando i vincoli di layout:

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

f.size   = s;

[ self  setFrame : f ];

0

Se il tuo tableHeaderView è un webView con contenuto regolabile, puoi provare:

[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;
}

L'ho testato su iOS9 e iOS11, ha funzionato bene.


-3

Hai provato [self.tableView reloadData]dopo aver cambiato l'altezza?


1
Si tratta di tableHeaderView, che è una visualizzazione statica. - [UITableView reloadData]è inteso solo per le viste dinamiche (celle) e anche la sezione Intestazioni che ovviamente pensavi fossero destinate;)
Julian F. Weinert
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.