web-dev-qa-db-fra.com

Est-il possible d'utiliser la disposition automatique avec UITableView tableHeaderView?

Depuis que j'ai découvert AutoLayout, je l'utilise partout, maintenant j'essaie de l'utiliser avec un tableHeaderView.

J'ai créé un subclass de UIView tout ajouté (étiquettes, etc.) que je voulais avec leurs contraintes, puis j'ai ajouté ce CustomView à la UITableView'tableHeaderView.

Tout fonctionne très bien, sauf que UITableView affiche toujours ci-dessus le CustomView, par ci-dessus je veux dire que CustomView est sous _ __ UITableView et ne peut donc pas être vu!

Il semble que peu importe ce que je fais, le height du UITableView'tableHeaderView est toujours 0 (tout comme la largeur, x et y).

Ma question: est-il possible d'accomplir ceci sans régler le cadre manuellement?

EDIT: Le CustomView'subview que j'utilise a les contraintes suivantes:

_title = [[UILabel alloc]init];
_title.text = @"Title";
[self addSubview:_title];
[_title keep:[KeepTopInset rules:@[[KeepEqual must:5]]]]; // title has to stay at least 5 away from the supperview Top
[_title keep:[KeepRightInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepLeftInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepBottomInset rules:@[[KeepMin must:5]]]];

J'utilise une bibliothèque pratique 'KeepLayout' car l'écriture manuelle de contraintes prend une éternité et est bien trop longue pour une seule contrainte, mais les méthodes s'expliquent d'elles-mêmes.

Et le UITableView a ces contraintes:

_tableView = [[UITableView alloc]init];
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_tableView];
[_tableView keep:[KeepTopInset rules:@[[KeepEqual must:0]]]];// These 4 constraints make the UITableView stays 0 away from the superview top left right and bottom.
[_tableView keep:[KeepLeftInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepRightInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepBottomInset rules:@[[KeepEqual must:0]]]];

_detailsView = [[CustomView alloc]init];
_tableView.tableHeaderView = _detailsView;

Je ne sais pas si je dois définir des contraintes directement sur le CustomView. Je pense que la hauteur de CustomView est déterminée par les contraintes sur le UILabel "titre" qu'il contient.

EDIT 2: Après une autre enquête, il semble que la hauteur et la largeur du CustomView soient correctement calculées, mais le haut du CustomView est toujours au même niveau que celui du haut de UITableView et ils bougent ensemble lorsque je fais défiler l'écran. .

85
ItsASecret

J'ai demandé et répondu à une question similaire ici . En résumé, j'ajoute l'en-tête une fois et l'utilise pour trouver la hauteur requise. Cette hauteur peut ensuite être appliquée à l'en-tête, lequel est défini une seconde fois pour refléter le changement.

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.header = [[SCAMessageView alloc] init];
    self.header.titleLabel.text = @"Warning";
    self.header.subtitleLabel.text = @"This is a message with enough text to span multiple lines. This text is set at runtime and might be short or long.";

    //set the tableHeaderView so that the required height can be determined
    self.tableView.tableHeaderView = self.header;
    [self.header setNeedsLayout];
    [self.header layoutIfNeeded];
    CGFloat height = [self.header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    //update the header's frame and set it again
    CGRect headerFrame = self.header.frame;
    headerFrame.size.height = height;
    self.header.frame = headerFrame;
    self.tableView.tableHeaderView = self.header;
}

Si vous avez des étiquettes multilignes, cela dépend également de l'affichage personnalisé définissant la couleur preferredMaxLayoutWidth de chaque étiquette:

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
    self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}

ou peut-être plus généralement:

override func layoutSubviews() {
    super.layoutSubviews()  
    for view in subviews {
        guard let label = view as? UILabel where label.numberOfLines == 0 else { continue }
        label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame)
    }
}

Mise à jour janvier 2015

Malheureusement, cela semble encore nécessaire. Voici une version Swift du processus de mise en page:

tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header

J'ai trouvé utile de déplacer ceci dans une extension sur UITableView:

extension UITableView {
    //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
    func setAndLayoutTableHeaderView(header: UIView) {
        self.tableHeaderView = header
        header.setNeedsLayout()
        header.layoutIfNeeded()
        header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
        self.tableHeaderView = header
    }
}

Usage:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)
120
Ben Packard

J'ai été incapable d'ajouter une vue d'en-tête à l'aide de contraintes (en code). Si je donne à ma vue une contrainte de largeur et/ou de hauteur, le message suivant s'affiche:

 "terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."

Lorsque j'ajoute une vue dans le storyboard à ma vue tableau, elle ne montre aucune contrainte et fonctionne correctement comme vue d'en-tête. Je pense donc que l'emplacement de la vue d'en-tête n'est pas effectué à l'aide de contraintes. Cela ne semble pas se comporter comme une vision normale à cet égard.

La largeur correspond automatiquement à la largeur de la vue tableau. La seule chose que vous devez définir est la hauteur. Les valeurs d'origine sont ignorées. Par conséquent, peu importe ce que vous insérez dans ces paramètres. Par exemple, cela a bien fonctionné (comme 0,0,0,80 pour le rect):

UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;
24
rdelmar

J'ai vu beaucoup de méthodes ici faire tellement de choses inutiles, mais vous n'avez pas besoin de beaucoup pour utiliser la présentation automatique dans la vue en-tête. Il vous suffit de créer votre fichier xib, de mettre vos contraintes et de l'instancier comme ceci: 

func loadHeaderView () {
        guard let headerView = Bundle.main.loadNibNamed("CourseSearchHeader", owner: self, options: nil)?[0] as? UIView else {
            return
        }
        headerView.autoresizingMask = .flexibleWidth
        headerView.translatesAutoresizingMaskIntoConstraints = true
        tableView.tableHeaderView = headerView
    }
8
Ramon Vasconcelos

Une autre solution consiste à répartir la création de la vue d’en-tête au prochain appel du thread principal:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

    dispatch_async(dispatch_get_main_queue(), ^{
        _profileView = [[MyView alloc] initWithNib:@"MyView.xib"];
        self.tableView.tableHeaderView = self.profileView;
    });
}

Remarque: le bogue est corrigé lorsque la vue chargée a une hauteur fixe. Je n'ai pas essayé de savoir quand la hauteur de l'en-tête dépend uniquement de son contenu.

MODIFIER :

Vous pouvez trouver une solution plus propre à ce problème en implémentant cette fonction function et en l’appelant dans viewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}
6
Martin

Des choses étranges se passent. systemLayoutSizeFittingSize fonctionne très bien pour iOS9, mais pas pour iOS 8 dans mon cas. Donc, ces problèmes sont résolus assez facilement. Il suffit d’obtenir un lien vers la vue du bas dans les en-têtes et dans viewDidLayoutSubviews après la super mise à jour de l’en-tête des appels en insérant height comme CGRectGetMaxY (yourview.frame) + padding

UPD: La solution la plus simple : Ainsi, dans la vue d’en-tête, placez la sous-vue et associez-la à left , right , top . Dans cette sous-vue, placez vos sous-vues avec des contraintes de hauteur automatique. Après cela, donnez tout le travail à l'autolayout (aucun calcul requis) 

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGFloat height = CGRectGetMaxY(self.tableView.tableHeaderView.subviews.firstObject.frame);
    self.tableView.tableHeaderView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
    self.tableView.tableHeaderView = self.tableView.tableHeaderView;
}

En conséquence, la sous-vue est agrandie/réduite comme il se doit, à la fin, elle appelle viewDidLayoutSubviews. Au moment où nous connaissons la taille réelle de la vue, définissez donc headerView height et mettez-la à jour en la réaffectant. Fonctionne comme un charme!

Fonctionne également pour l'affichage du pied de page.

3
HotJard

Code:

  extension UITableView {

          func sizeHeaderToFit(preferredWidth: CGFloat) {
            guard let headerView = self.tableHeaderView else {
              return
            }

            headerView.translatesAutoresizingMaskIntoConstraints = false
            let layout = NSLayoutConstraint(
              item: headerView,
              attribute: .Width,
              relatedBy: .Equal,
              toItem: nil,
              attribute:
              .NotAnAttribute,
              multiplier: 1,
              constant: preferredWidth)

            headerView.addConstraint(layout)

            let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
            headerView.frame = CGRectMake(0, 0, preferredWidth, height)

            headerView.removeConstraint(layout)
            headerView.translatesAutoresizingMaskIntoConstraints = true

            self.tableHeaderView = headerView
          }
  }
3
Phil

Vous pouvez demander à autolayout de vous fournir une taille en utilisant la méthode systemLayoutSizeFittingSize .

Vous pouvez ensuite l'utiliser pour créer le cadre de votre application. Cette technique fonctionne chaque fois que vous devez connaître la taille d'une vue utilisant l'autolayout en interne. 

Le code dans Swift ressemble à

//Create the view
let tableHeaderView = CustomTableHeaderView()

//Set the content
tableHeaderView.textLabel.text = @"Hello world"

//Ask auto layout for the smallest size that fits my constraints    
let size = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

//Create a frame    
tableHeaderView.frame = CGRect(Origin: CGPoint.zeroPoint, size: size)

//Set the view as the header    
self.tableView.tableHeaderView = self.tableHeaderView

Ou en Objective-C

//Create the view
CustomTableHeaderView *header = [[CustomTableHeaderView alloc] initWithFrame:CGRectZero];

//Set the content
header.textLabel.text = @"Hello world";

//Ask auto layout for the smallest size that fits my constraints
CGSize size = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

//Create a frame
header.frame = CGRectMake(0,0,size.width,size.height);

//Set the view as the header  
self.tableView.tableHeaderView = header

Il convient également de noter que dans ce cas particulier, le remplacement de requireConstraintBasedLayout dans votre sous-classe entraîne l'exécution d'une passe de mise en page. Toutefois, les résultats de cette passe sont ignorés et le cadre système défini sur la largeur de tableView et la hauteur 0.

2
Jonathan

Ce qui suit a fonctionné pour moi.

  1. Utilisez une vieille variable UIView comme vue d'en-tête.
  2. Ajouter des sous-vues à cette UIView
  3. Utiliser l'autolayout sur les sous-vues

Le principal avantage que je vois est de limiter les calculs de trame. Apple devrait vraiment mettre à jour l'API de UITableView pour rendre cela plus facile.

Exemple d'utilisation de SnapKit:

let layoutView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 60))
layoutView.backgroundColor = tableView.backgroundColor
tableView.tableHeaderView = layoutView

let label = UILabel()
layoutView.addSubview(label)
label.text = "I'm the view you really care about"
label.snp_makeConstraints { make in
    make.edges.equalTo(EdgeInsets(top: 10, left: 15, bottom: -5, right: -15))
}
2
David Nix

Extension de cette solution http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ pour l'affichage du pied de table

@interface AutolayoutTableView : UITableView

@end

@implementation AutolayoutTableView

- (void)layoutSubviews {
    [super layoutSubviews];

    // Dynamic sizing for the header view
    if (self.tableHeaderView) {
        CGFloat height = [self.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect headerFrame = self.tableHeaderView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != headerFrame.size.height) {
            headerFrame.size.height = height;
            self.tableHeaderView.frame = headerFrame;
            self.tableHeaderView = self.tableHeaderView;
        }

        [self.tableHeaderView layoutIfNeeded];
    }

    // Dynamic sizing for the footer view
    if (self.tableFooterView) {
        CGFloat height = [self.tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect footerFrame = self.tableFooterView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != footerFrame.size.height) {
            footerFrame.size.height = height;
            self.tableFooterView.frame = footerFrame;
            self.tableFooterView = self.tableFooterView;
        }

        self.tableFooterView.transform = CGAffineTransformMakeTranslation(0, self.contentSize.height - footerFrame.size.height);
        [self.tableFooterView layoutIfNeeded];
    }
}

@end
2
k06a

La vue en-tête de ma table est une sous-classe UIView - j'ai créé un seul contentView UIView dans l'initialiseur, avec ses limites identiques à celles du cadre de la vue d'en-tête de table et ajouté tous mes objets en tant que sous-vue.

Ajoutez ensuite les contraintes pour vos objets dans la méthode layoutSubviews de la vue en-tête de la table plutôt que dans l'initialiseur. Cela a résolu le crash.

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:CGRectMake(0, 0, 0, 44.0)];
    if (self) {
        UIView *contentView = [[UIView alloc] initWithFrame:self.bounds];
        contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

        // add other objects as subviews of content view

    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    // remake constraints here
}
1
Ryan

vous pouvez ajouter une contrainte d'emplacement top + horizontal entre l'en-tête et la table, pour la placer correctement (si l'en-tête contient toutes les contraintes de présentation internes nécessaires pour avoir un cadre correct)

dans la méthode tableViewController viewDidLoad

    headerView.translatesAutoresizingMaskIntoConstraints = false

    tableView.tableHeaderView = headerView

    headerView.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
    headerView.topAnchor.constraint(equalTo: tableView.topAnchor).isActive = true
    headerView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor).isActive = true
1
GreatWiz

Partagez mon approche.

UITableView+XXXAdditions.m

- (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock {
      CGFloat containerViewHeight = 0;
      UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
      [backgroundView addSubview:tableHeaderView];
      layoutBlock(tableHeaderView, &containerViewHeight);

      backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight);

      self.tableHeaderView = backgroundView;
}

Usage. 

[self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) {
    *containerViewHeight = 170;

    [tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
      make.top.equalTo(@20);
      make.centerX.equalTo(@0);
      make.size.mas_equalTo(CGSizeMake(130, 130));
    }];
  }];
0
Vincent

J'ai été capable d'y parvenir par l'approche suivante (cela fonctionne pour le pied de page de la même manière).

Tout d’abord, vous aurez besoin d’une petite extension UITableView:

Swift 3

extension UITableView {
    fileprivate func adjustHeaderHeight() {
        if let header = self.tableHeaderView {
            adjustFrame(header)
        }
    }

    private func adjustFrame(_ view: UIView) {
        view.frame.size.height = calculatedViewHeight(view)
    }

    fileprivate func calculatedHeightForHeader() -> CGFloat {
        if let header = self.tableHeaderView {
            return calculatedViewHeight(header)
        }
        return 0.0
    }

    private func calculatedViewHeight(_ view: UIView) -> CGFloat {
        view.setNeedsLayout()
        let height = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        return height
    }
}

Dans votre vue, implémentation de la classe du contrôleur:

// this is a UIView subclass with autolayout
private var headerView = MyHeaderView()

override func loadView() {
    super.loadView()
    // ...
    self.tableView.tableHeaderView = headerView
    self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension
    // ...
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    // this is to prevent recursive layout calls
    let requiredHeaderHeight = self.tableView.calculatedHeightForHeader()
    if self.headerView.frame.height != requiredHeaderHeight {
        self.tableView.adjustHeaderHeight()
    }
}

Remarques sur l'implémentation de la sous-vue UIView de l'en-tête __:

  1. Vous devez vous assurer à 100% que votre vue en-tête dispose de la configuration correcte de la lecture automatique. Je recommanderais de commencer par une vue d'en-tête simple avec une seule contrainte de hauteur et d'essayer la configuration ci-dessus. 

  2. Remplacez requiresConstraintBasedLayout et retournez true:

.

class MyHeaderView: UIView {
   // ...
   override static var requiresConstraintBasedLayout : Bool {
       return true
   }
   // ...
}
0
Yevhen Dubinin

Je sais que c’est un vieux billet, mais après avoir passé en revue tous les billets SO à ce sujet et passé un après-midi entier à jouer avec cela, j’ai finalement trouvé une solution propre et pourtant très simple 

Tout d'abord, ma hiérarchie de vues ressemble à ceci:

  1. Table View
    1. Voir tableHeaderView
      1. Voir avec une sortie appelée headerView 

Maintenant, à l'intérieur de la vue (n ° 3), j'ai configuré toutes les contraintes comme je le ferais normalement, y compris l'espace du bas dans le conteneur. Cela fera que le conteneur (c'est-à-dire 3.View, c'est-à-dire headerView) se redimensionne lui-même en fonction de ses sous-vues et de leurs contraintes.

Après cela, je règle les contraintes entre 3. View et 2. View à celles-ci:

  1. Top espace vers le conteneur: 0
  2. Espace principal dans le conteneur: 0
  3. Espace de fuite vers le conteneur: 0

Notez que j'omets intentionnellement l'espace du bas intentionnellement.

Une fois que tout cela est fait dans le scénario, tout ce qui reste à faire est de coller ces trois lignes de codes:

if (self.headerView.frame.size.height != self.tableView.tableHeaderView.frame.size.height) {
    UIView *header = self.tableView.tableHeaderView;
    CGRect frame = self.tableView.tableHeaderView.frame;
    frame.size.height = self.headerView.frame.size.height + frame.Origin.y;
    header.frame = frame;
    self.tableView.tableHeaderView = header;
}
0

Voici comment vous pouvez le faire dans votre UIViewController

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if headerView.frame.size.height == 0 {
      headerView.label.preferredMaxLayoutWidth = view.bounds.size.width - 20
      let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height

      headerView.frame.size = CGSize(width: tableView.bounds.size.width, height: height)
    }
  }
0
onmyway133

J'ai trouvé une solution de contournement. encapsulez votre vue d'en-tête xib d'écriture autolayout dans un wrapper uiview vide et affectez la vue d'en-tête à la propriété tableViewHeader de tableView. 

    UIView *headerWrapper = [[UIView alloc] init];
    AXLHomeDriverHeaderView *headerView = [AXLHomeDriverHeaderView loadViewFromNib];
    [headerWrapper addSubview:headerView];
    [headerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(headerWrapper);
    }];
    self.tableView.tableHeaderView = headerView;
0
tounaobun

Un vieux post. Mais un bon post. Voici mes 2 cents.

Tout d'abord, assurez-vous que la vue de votre en-tête a ses contraintes organisées de manière à pouvoir prendre en charge sa propre taille de contenu intrinsèque. Ensuite, procédez comme suit.

//ViewDidLoad
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.configure(title: "Some Text A")

//Somewhere else
headerView.update(title: "Some Text B)

private var widthConstrained = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if widthConstrained == false {
        widthConstrained = true
        tableView.addConstraint(NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: tableView, attribute: .width, multiplier: 1, constant: 0))
        headerView.layoutIfNeeded()
        tableView.layoutIfNeeded()
    }
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { (context) in
        self.headerView.layoutIfNeeded()
        self.tableView.layoutIfNeeded()
    }, completion: nil)
}
0
user1951992

Dans mon cas, la méthode avec systemLayoutSizeFittingSize pour une raison quelconque ne fonctionnait pas. Ce qui a fonctionné pour moi, c’est une modification de solution publiée par HotJard (sa solution originale n’a pas non plus fonctionné dans mon cas sous iOS 8). Ce que je devais faire, c’est que dans la vue en-tête, placez une sous-vue et fixez-la à gauche, à droite, en haut (ne pas en bas). Mettez tout en utilisant autolayout dans cette sous-vue et dans le code procédez comme suit:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [self resizeHeaderToFitSubview];
}

- (void)resizeHeaderToFitSubview
{
    UIView *header = self.tableView.tableHeaderView;
    [header setNeedsLayout];
    [header layoutIfNeeded];
    CGFloat height = CGRectGetHeight(header.subviews.firstObject.bounds);
    header.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
    self.tableView.tableHeaderView = nil;
    self.tableView.tableHeaderView = header;
}
0
Leszek Szary

Mis à jour pour Swift 4.2

extension UITableView {

    var autolayoutTableViewHeader: UIView? {
        set {
            self.tableHeaderView = newValue
            guard let header = newValue else { return }
            header.setNeedsLayout()
            header.layoutIfNeeded()
            header.frame.size = 
            header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
            self.tableHeaderView = header
        }
        get {
            return self.tableHeaderView
        }
    }
}
0
Caleb Friden

J'ai rencontré le problème d'obtenir 375 pt de largeur, la seule façon qui a fonctionné pour moi est de relayer la tableView pour obtenir la largeur correcte. J'ai aussi préféré AutoLayout que la taille du cadre.

Voici la version qui fonctionne pour moi:

Xamarin.iOS

public static void AutoLayoutTableHeaderView(this UITableView tableView, UIView header)
{
    tableView.TableHeaderView = header;
    tableView.SetNeedsLayout();
    tableView.LayoutIfNeeded();
    header.WidthAnchor.ConstraintEqualTo(tableView.Bounds.Width).Active = true;       
    tableView.TableHeaderView = header;
}

Version Swift (modifiée à partir de la réponse @Ben Packard)

extension UITableView {
    //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
    func setAndLayoutTableHeaderView(header: UIView) {
        self.tableHeaderView = header
        self.setNeedsLayout()
        self.layoutIfNeeded()
        header.widthAnchor.widthAnchor.constraint(equalTo: self.bounds.width).isActive = true
        self.tableHeaderView = header
    }
}
0
Baron Ch'ng

Voici ce qui fonctionne pour UITableViewController dans ios 12, 

Drapez un UIView dans la vue Table avant toutes les cellules prototypes pour l’en-tête et sous toutes les cellules prototypes pour le pied de page. Configurez votre en-tête et votre pied de page si nécessaire. Définissez toutes les contraintes requises. 

Maintenant, utilisez les méthodes d'extension suivantes

public static class UITableVIewExtensions
{

    public static void MakeHeaderAutoDimension(this UITableView tableView)
    {
        if (tableView.TableHeaderView is UIView headerView) {
            var size = headerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
            if (headerView.Frame.Size.Height != size.Height) {
                var frame = headerView.Frame;
                frame.Height = size.Height;
                headerView.Frame = frame;
                tableView.TableHeaderView = headerView;
                tableView.LayoutIfNeeded();
            }
        }
    }

    public static void MakeFooterAutoDimension(this UITableView tableView)
    {
        if (tableView.TableFooterView is UIView footerView) {
            var size = footerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
            if (footerView.Frame.Size.Height != size.Height) {
                var frame = footerView.Frame;
                frame.Height = size.Height;
                footerView.Frame = frame;
                tableView.TableFooterView = footerView;
                tableView.LayoutIfNeeded();
            }
        }
    }
}

et appelez-le dans ViewDidLayoutSubviews de la sous-classe de UITableViewController

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableView.MakeHeaderAutoDimension();
    TableView.MakeFooterAutoDimension();
}
0
HeisenBerg

Pour les utilisateurs de Xamarin:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableviewHeader.SetNeedsLayout();
    TableviewHeader.LayoutIfNeeded();

    var height = TableviewHeader.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize).Height;
    var frame = TableviewHeader.Frame;
    frame.Height = height;
    TableviewHeader.Frame = frame;
}

En supposant que vous ayez nommé la vue d’en-tête de votre vue table comme TableviewHeader

0

Mon AutoLayout fonctionne très bien:

CGSize headerSize = [headerView systemLayoutSizeFittingSize:CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
headerView.frame = CGRectMake(0, 0, headerSize.width, headerSize.height);
self.tableView.tableHeaderView = headerView;
0
RomanV

Conseils: Si vous utilisez la méthode setAndLayoutTableHeaderView, vous devez mettre à jour le cadre des sous-vues. Par conséquent, dans cette situation, PreferredMaxLayoutWidth de UILabel doit appeler avant l'appel de systemLayoutSizeFittingSize, n'appelez pas layoutSubview.

code show

0
user1511613

Toute UIView basée sur une contrainte peut être une bonne tableHeaderView.

Il faut définir un tableFooterView avant, puis imposer une contrainte supplémentaire sur tableFooterView et tableHeaderView.

- (void)viewDidLoad {

    ........................
    // let self.headerView is some constraint-based UIView
    self.tableView.tableFooterView = [UIView new];
    [self.headerView layoutIfNeeded];
    self.tableView.tableHeaderView = self.headerView;

    [self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES;
    [self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
    [self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES;
    [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;

}

On peut trouver tous les détails et extraits de code ici

0
malex