Question Comment définir la hauteur de tableHeaderView (UITableView) avec autolayout?


Cela fait 3 ou 4 heures que je brise la tête contre le mur et je n'arrive pas à comprendre. J'ai un UIViewController avec un UITableView en plein écran à l'intérieur (il y a d'autres choses à l'écran, c'est pourquoi je ne peux pas utiliser un UITableViewController) et je veux redimensionner mon tableHeaderView avec autolayout. Inutile de dire que cela ne coopère pas.

Voir la capture d'écran ci-dessous.

enter image description here

Étant donné que overviewLabel (par exemple, le texte "Informations de vue d'ensemble ici".) Contient du contenu dynamique, j'utilise autolayout pour le redimensionner et sa vue d'ensemble. J'ai tout redimensionné, sauf pour la tableHeaderView, qui se trouve juste en dessous de la vue de table Paralax dans la hiérarchie.

La seule façon dont j'ai trouvé pour redimensionner cette vue d'en-tête est par programme, avec le code suivant:

CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];

Dans ce cas, headerFrameHeight est un calcul manuel de la hauteur de tableViewHeader comme suit (innerHeaderView est la zone blanche, ou la seconde "vue", headerView est tableHeaderView):

CGFloat startingY = self.innerHeaderView.frame.origin.y + self.overviewLabel.frame.origin.y;
CGRect overviewSize = [self.overviewLabel.text
                       boundingRectWithSize:CGSizeMake(290.f, CGFLOAT_MAX)
                       options:NSStringDrawingUsesLineFragmentOrigin
                       attributes:@{NSFontAttributeName: self.overviewLabel.font}
                       context:nil];
CGFloat overviewHeight = overviewSize.size.height;
CGFloat overviewPadding = ([self.overviewLabel.text length] > 0) ? 10 : 0; // If there's no overviewText, eliminate the padding in the overall height.
CGFloat headerFrameHeight = ceilf(startingY + overviewHeight + overviewPadding + 21.f + 10.f);

Le calcul manuel fonctionne, mais il est maladroit et sujet aux erreurs si les choses changent dans le futur. Ce que je veux pouvoir faire est de redimensionner automatiquement la tableHeaderView en fonction des contraintes fournies, comme vous pouvez le faire ailleurs. Mais pour la vie de moi, je ne peux pas le comprendre.

Il y a plusieurs articles sur SO à ce sujet, mais aucun n'est clair et a fini par me confondre davantage. Voici quelques uns:

Cela n'a pas vraiment de sens de changer la propriété translatesAutoresizingMaskIntoConstraints à NO, car cela ne cause que des erreurs pour moi et n'a pas de sens conceptuel de toute façon.

Toute aide serait vraiment appréciée!

EDIT 1: Grâce à la suggestion de TomSwift, j'ai pu le comprendre. Au lieu de calculer manuellement la hauteur de la vue d'ensemble, je peux la calculer comme suit, puis rétablir la tableHeaderView comme précédemment.

[self.headerView setNeedsLayout];
[self.headerView layoutIfNeeded];
CGFloat height = [self.innerHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + self.innerHeaderView.frame.origin.y; // adding the origin because innerHeaderView starts partway down headerView.

CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = height;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];

Edit 2: Comme d'autres l'ont noté, la solution publiée dans Edit 1 ne semble pas fonctionner dans viewDidLoad. Cela semble cependant fonctionner dans viewWillLayoutSubviews. Exemple de code ci-dessous:

// Note 1: The variable names below don't match the variables above - this is intended to be a simplified "final" answer.
// Note 2: _headerView was previously assigned to tableViewHeader (in loadView in my case since I now do everything programatically).
// Note 3: autoLayout code can be setup programatically in updateViewConstraints.
- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    [_headerWrapper setNeedsLayout];
    [_headerWrapper layoutIfNeeded];
    CGFloat height = [_headerWrapper systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    CGRect headerFrame = _headerWrapper.frame;
    headerFrame.size.height = height;
    _headerWrapper.frame = headerFrame;
    _tableView.tableHeaderView = _headerWrapper;
}

79
2018-01-07 21:44


origine


Réponses:


Vous devez utiliser le UIView systemLayoutSizeFittingSize: méthode pour obtenir la taille minimale de la vue d'en-tête.

Je propose une discussion plus approfondie sur l’utilisation de cette API dans ce questionnaire:

Comment redimensionner superview pour l'adapter à toutes les sous-vues avec autolayout?


34
2018-01-08 01:10



Je me suis vraiment battu avec celui-ci et en plaçant la configuration dans viewDidLoad ne fonctionnait pas pour moi car le frame n'est pas défini dans viewDidLoad, j'ai aussi fini avec des tonnes d'avertissements désordonnés où la hauteur de mise en page automatique encapsulée était réduite à 0 J'ai seulement remarqué le problème sur iPad lors de la présentation d'une tableView dans une présentation de formulaire.

Ce qui a résolu le problème pour moi était de définir la tableViewHeader dans viewWillLayoutSubviews plutôt que dans viewDidLoad.

func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        if tableView.tableViewHeaderView == nil {
            let header: MyHeaderView = MyHeaderView.createHeaderView()
            header.setNeedsUpdateConstraints()
            header.updateConstraintsIfNeeded()
            header.frame = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGFloat.max)
            var newFrame = header.frame
            header.setNeedsLayout()
            header.layoutIfNeeded()
            let newSize = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
            newFrame.size.height = newSize.height
            header.frame = newFrame
            self.tableView.tableHeaderView = header
        }
    }

20
2018-02-10 12:56



J'ai trouvé une manière élégante d'utiliser la mise en page automatique pour redimensionner les en-têtes de tableau, avec et sans animation.

Ajoutez simplement ceci à votre View Controller.

func sizeHeaderToFit(tableView: UITableView) {
    if let headerView = tableView.tableHeaderView {
        let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
        var frame = headerView.frame
        frame.size.height = height
        headerView.frame = frame
        tableView.tableHeaderView = headerView
        headerView.setNeedsLayout()
        headerView.layoutIfNeeded()
    }
}

Pour redimensionner selon une étiquette changeant dynamiquement:

@IBAction func addMoreText(sender: AnyObject) {
    self.label.text = self.label.text! + "\nThis header can dynamically resize according to its contents."
}

override func viewDidLayoutSubviews() {
    // viewDidLayoutSubviews is called when labels change.
    super.viewDidLayoutSubviews()
    sizeHeaderToFit(tableView)
}

Pour animer un redimensionnement en fonction d'une modification dans une contrainte:

@IBOutlet weak var makeThisTallerHeight: NSLayoutConstraint!

@IBAction func makeThisTaller(sender: AnyObject) {

    UIView.animateWithDuration(0.3) {
        self.tableView.beginUpdates()
        self.makeThisTallerHeight.constant += 20
        self.sizeHeaderToFit(self.tableView)
        self.tableView.endUpdates()
    }
}

Voir le projet AutoResizingHeader pour voir cela en action. https://github.com/p-sun/Swift2-iOS9-UI

AutoResizingHeader


12
2017-07-16 13:29



Votre solution à l'aide de systemLayoutSizeFittingSize: fonctionne si la vue d'en-tête est simplement mise à jour une fois sur chaque apparence de vue. Dans mon cas, la vue d'en-tête a été mise à jour plusieurs fois pour refléter les changements d'état. Mais systemLayoutSizeFittingSize: a toujours signalé la même taille. C'est-à-dire la taille correspondant à la première mise à jour.

Pour que systemLayoutSizeFittingSize: renvoie la taille correcte après chaque mise à jour, j'ai d'abord dû supprimer la vue d'en-tête de la table avant de la mettre à jour et de la rajouter:

self.listTableView.tableHeaderView = nil;
[self.headerView removeFromSuperview];

3
2018-03-11 12:40



Cela a fonctionné pour moi sur ios10 et Xcode 8

func layoutTableHeaderView() {

    guard let headerView = tableView.tableHeaderView else { return }
    headerView.translatesAutoresizingMaskIntoConstraints = false

    let headerWidth = headerView.bounds.size.width;
    let temporaryWidthConstraints = NSLayoutConstraint.constraintsWithVisualFormat("[headerView(width)]", options: NSLayoutFormatOptions(rawValue: UInt(0)), metrics: ["width": headerWidth], views: ["headerView": headerView])

    headerView.addConstraints(temporaryWidthConstraints)

    headerView.setNeedsLayout()
    headerView.layoutIfNeeded()

    let headerSize = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
    let height = headerSize.height
    var frame = headerView.frame

    frame.size.height = height
    headerView.frame = frame

    self.tableView.tableHeaderView = headerView

    headerView.removeConstraints(temporaryWidthConstraints)
    headerView.translatesAutoresizingMaskIntoConstraints = true

}

2
2017-11-11 21:00



Cette solution redimensionne la tableHeaderView et évite la boucle infinie dans le viewDidLayoutSubviews() méthode que j'avais avec certaines des autres réponses ici:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let headerView = tableView.tableHeaderView {
        let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        var headerFrame = headerView.frame

        // comparison necessary to avoid infinite loop
        if height != headerFrame.size.height {
            headerFrame.size.height = height
            headerView.frame = headerFrame
            tableView.tableHeaderView = headerView
        }
    }
}

Voir aussi ce post: https://stackoverflow.com/a/34689293/1245231


1
2017-08-28 21:48



Dans mon cas viewDidLayoutSubviews mieux travaillé. viewWillLayoutSubviews provoque des lignes blanches d'un tableView apparaître. J'ai aussi ajouté vérifier si mon headerView objet existe déjà.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    if ( ! self.userHeaderView ) {
        // Setup HeaderView
        self.userHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"SSUserHeaderView" owner:self options:nil] objectAtIndex:0];
        [self.userHeaderView setNeedsLayout];
        [self.userHeaderView layoutIfNeeded];
        CGFloat height = [self.userHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect headerFrame = self.userHeaderView.frame;
        headerFrame.size.height = height;
        self.userHeaderView.frame = headerFrame;
        self.tableView.tableHeaderView = self.userHeaderView;

        // Update HeaderView with data
        [self.userHeaderView updateWithProfileData];
    }
}

0
2017-12-07 17:01