Comment redimensionner une tableHeaderView d'un UITableView?

103

Je rencontre des difficultés pour redimensionner une tableHeaderView. Cela ne fonctionne pas simplement.

1) Créez un UITableView et UIView (100 x 320 px);

2) Définissez l'UIView comme tableHeaderView de UITableView;

3) Construisez et partez. Tout va bien.

Maintenant, je veux redimensionner le tableHeaderView, donc j'ajoute ce code dans 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 hauteur de tableHeaderView doit apparaître avec 200, mais apparaît avec 100.

Si j'écris:

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;

Ensuite, ça commence par 200 de hauteur, comme je veux. Mais je veux pouvoir le modifier en cours d'exécution.

J'ai aussi essayé ceci, sans succès:

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

Le point ici est: Comment redimensionner une tableHeaderView au runtime ???

Quelqu'un est-il capable de faire cela?

Merci

iMe

iMe
la source

Réponses:

180

FYI: J'ai réussi à faire fonctionner cela en modifiant le tableHeaderView et en le réinitialisant. Dans ce cas, j'ajuste la taille de tableHeaderView lorsque la sous-vue UIWebView a terminé le chargement.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];
kubi
la source
13
+1 Cela a fonctionné pour moi. L'appel de «setTableHeaderView» après que votre sous-vue a changé de taille est la clé. Le problème est que ma sous-vue change de taille en une seconde sous forme d'animation. Maintenant, j'essaie de comprendre comment animer la tableHeaderView avec.
Andrew
1
Parfait, merci beaucoup. Pour moi, c'est un exemple de l'une des qualités les moins souhaitables des propriétés en Objective-C. Il n'y a aucun moyen pour nous de savoir (et aucune raison pour laquelle nous devrions savoir) que la définition de l'en-tête a pour effet secondaire de recalculer la hauteur. Il devrait soit le faire automatiquement lorsque nous mettons à jour la hauteur de l'en-tête, soit nous devrions être obligés d'appeler quelque chose comme à [tableView recalculateHeaderHeight]chaque fois.
jakeboxer
48
@Andrew Je sais que c'est presque un an trop tard, mais mieux vaut tard que jamais: j'ai pu animer la hauteur changeante de la vue de l'en-tête du tableau en terminant l' setTableHeaderView:appel avec [tableview beginUpdates]et[tableview endUpdates]
jasongregori
2
@jasongregori commentaire pratique que vous avez ajouté - je vois que la même technique (beginUpdates + endUpdates) n'anime pas le changement de hauteur pour un tableFooterView comme il le fait pour un tableHeaderView. Avez-vous également trouvé un bon moyen d'animer le tableauFooterView?
kris
1
Impressionnant, cet événement fonctionne lorsque vous le faites dans un bloc d'animation UIView, même si je ne pense pas que ce soit très efficace dans ce cas.
Can
12

Cette réponse est ancienne et ne fonctionne apparemment pas sur iOS 7 et supérieur.

J'ai rencontré le même problème et je voulais également que les modifications s'animent, j'ai donc créé une sous-classe de UIView pour ma vue d'en-tête et ajouté ces méthodes:

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

Cela anime essentiellement toutes les sous-vues de UITableView qui ne sont pas du même type de classe que la classe appelante. À la fin de l'animation, il appelle setTableHeaderView sur le superview (l'UITableView) - sans cela, le contenu de UITableView reviendra la prochaine fois que l'utilisateur fera défiler. La seule limitation que j'ai trouvée à ce sujet jusqu'à présent est que si l'utilisateur tente de faire défiler l'UITableView pendant que l'animation est en cours, le défilement s'animera comme si la vue d'en-tête n'avait pas été redimensionnée (ce qui n'est pas grave si l'animation est rapide).

Garrettmoon
la source
fonctionne parfaitement, a supprimé l'animation car je n'en avais pas besoin.
Jiho Kang
A fonctionné parfaitement jusqu'à ce que iOS7 arrive ... a ajouté ma solution ci
avishic
1
WTF? Vous dérangez tellement les éléments internes de UITableView que vous ne devriez vraiment pas être surpris que cela ne fonctionne pas dans les nouvelles versions iOS ...;)
Daniel Rinser
10

Si vous souhaitez animer les modifications de manière conditionnelle, vous pouvez effectuer les opérations suivantes:

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

Veuillez noter que l'animation est double:

  1. L'animation des cellules sous le tableHeaderView. Ceci est fait en utilisant beginUpdatesetendUpdates
  2. L'animation de la vue d'en-tête réelle. Cela se fait à l'aide d'un UIViewbloc d'animation.

Afin de synchroniser ces deux animations, le animationCurvedoit être défini sur UIViewAnimationCurveEaseInOutet la durée sur 0.3, ce qui semble être ce que UITableView utilise pour son animation.

Mettre à jour

J'ai créé un projet Xcode sur gihub, qui fait cela. Découvrez le projet ResizeTableHeaderViewAnimateddans besi / ios-quickies

capture d'écran

Besi
la source
2
Cette technique fonctionne, mais elle ne fonctionne pas pour les vues de table avec des en-têtes de section. Lorsque vous utilisez les mises à jour de début / de fin, cela interfère avec le bloc d'animation, laissant des en-têtes de section en double.
BlueFish
9

Je pense que cela devrait fonctionner si vous définissez simplement la hauteur de myHeaderView comme suit:

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

self.tableView.tableHeaderView = myHeaderView;
Greg Martin
la source
Cela fonctionne réellement, mais seulement s'il est utilisé dans viewDidLayoutSubviews
KoCMoHaBTa
6

Utilisation de la solution @garrettmoon ci-dessus jusqu'à iOS 7.
Voici une solution mise à jour basée sur @ 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];
}
avishique
la source
5

Cela a fonctionné pour moi sur iOS 7 et 8. Ce code s'exécute sur le contrôleur de vue de table.

[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
la source
4

C'est parce que le setter de tableHeaderView.

Vous devez définir la hauteur UIView avant de définir tableHeaderView. (Ce serait beaucoup plus facile si Apple ouvrait les sources de ce cadre ...)

Thomas Decaux
la source
4

Sur iOS 9 et les versions antérieures, tableHeaderViewne remettrait pas en page après le redimensionnement. Ce problème est résolu dans iOS 10.

Pour résoudre ce problème, faites-le simplement avec le code suivant:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;
Klaudz
la source
2

Sur iOS 9.x, cela viewDidLoadfonctionne très bien:

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

headerViewest déclaré @IBOutlet var headerView: UIView!et connecté sur le storyboard, où il est placé en haut de tableView, pour fonctionner comme tableHeaderView.

Eneko Alonso
la source
2

C'est uniquement lorsque vous utilisez la mise en page automatique et que vous définissez translatesAutoresizingMaskIntoConstraints = falseune vue d'en-tête personnalisée.

Le moyen le meilleur et le plus simple est de passer outre intrinsicContentSize. En interne UITableViewutilise intrinsicContentSizepour décider de sa taille en- tête / pied de page. Une fois que vous avez remplacé intrinsicContentSizedans votre vue personnalisée, ce que vous devez faire est comme ci-dessous

  1. configurer la disposition de la vue d'en-tête / pied de page personnalisée (sous-vues)
  2. invoquer invalidateIntrinsicContentSize()
  3. invoquer tableView.setNeedsLayout()ettableView.layoutIfNeeded()

Ensuite, l UITableView'en - tête / pied de page sera mis à jour comme vous le souhaitez. Pas besoin de définir la vue nulle ou de réinitialiser.

Une chose vraiment intéressante pour le UITableView.tableHeaderViewou .tableFooterViewest qu'il UIStackViewperd sa capacité à gérer son arrangedSubviews. Si vous souhaitez utiliser UIStackViewcomme tableHeaderView ou tableFooterView, vous devez intégrer le stackView dans une UIViewet override UIView« s intrinsicContentSize.

Ryan
la source
2

Pour le code testé Swift 5

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

Remarque: vous devez indiquer toutes les contraintes de la sous-vue sous forme haut, bas, début, fin. Ainsi, il obtiendra toute la taille requise.

Référence tirée de: https://useyourloaf.com/blog/variable-height-table-view-header/

Hardik Thakkar
la source
1

La définition de la hauteur de la propriété de vue d'en-tête tableView.tableHeaderViewdans viewDidLoadne semble pas fonctionner, la hauteur de vue d'en-tête ne change toujours pas comme prévu.

Après avoir combattu ce problème pendant de nombreux essais. J'ai trouvé que vous pouvez changer la hauteur en invoquant la vue d'en-tête pour créer une logique à l'intérieur de la - (void)didMoveToParentViewController:(UIViewController *)parentméthode.

Ainsi, l'exemple de code ressemblerait à ceci:

- (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
la source
0

J'ai trouvé que l'initialiseur initWithFrame d'un UIView n'honorait pas correctement le droit que je transmettais. Par conséquent, j'ai fait ce qui suit qui a parfaitement fonctionné:

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

        // ...
    }
}

L'avantage de ceci est qu'il est mieux encapsulé dans votre code de vue.

Harald Schubert
la source
Récupérer le cadre UIScreenest une mauvaise idée, comment allez-vous réutiliser votre vue?
Zorayr le
0

J'ai implémenté un changement de hauteur animé de l'en-tête de la table pour s'étendre à l'écran global lorsque vous appuyez dessus. Cependant, le code peut aider dans d'autres cas:

// 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
}
Alexandre Volkov
la source
0

En-tête de redimensionnement UITableView - UISearchBar avec Scope Bar

Je voulais un UITableViewavec un UISearchBarcomme en-tête du tableau, donc j'ai une hiérarchie qui ressemble à ceci

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

Méthodes UISearchBarDelegate

Comme indiqué ailleurs, si vous ne définissez pasTableViewHeader après l'avoir modifié, rien ne se passera.

- (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
la source
0

Si headerView personnalisé est conçu à l'aide de la mise en page automatique et que headerView doit être mis à jour après la récupération Web ou une tâche paresseuse similaire. puis dans iOS-Swift j'ai fait cela et j'ai mis à jour mon headerView en utilisant le code ci-dessous:

//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
la source
0

De toute évidence, Apple devrait maintenant avoir implémenté UITableViewAutomaticDimension pour tableHeaderView & tableFooterView ...

Ce qui suit semble fonctionner pour moi en utilisant des contraint (s) de mise en page:

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

f.size   = s;

[ self  setFrame : f ];
démon numérique
la source
0

Si votre tableHeaderView est une vue Web à contenu ajustable, vous pouvez essayer:

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

Je l'ai testé sur iOS9 et iOS11, a bien fonctionné.

无 夜 之 星辰
la source
-3

Avez-vous essayé [self.tableView reloadData]après avoir changé la hauteur?

codelogic
la source
1
Il s'agit de tableHeaderView, qui est une vue statique. - [UITableView reloadData]est uniquement destiné aux vues dynamiques (cellules) et aussi à la sectionHeaders que vous pensiez évidemment signifier;)
Julian F. Weinert