114 votes

Est-il possible d'utiliser AutoLayout avec le tableHeaderView de UITableView ?

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

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

Tout fonctionne bien, sauf le UITableView affiche toujours au-dessus de el CustomView par au-dessus de Je veux dire le CustomView es sous el UITableView pour qu'on ne puisse pas le voir !

Il semble que peu importe ce que je fais, les height de la UITableView ' tableHeaderView es toujours 0 (ainsi que la largeur, x et y).

Ma question : est-il possible d'accomplir cette tâche ? sans régler le cadre manuellement ?

EDIT : Le site CustomView ' subview que j'utilise a ces contraintes :

_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 écrire des contraintes manuellement prend une éternité et nécessite beaucoup trop de lignes 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 l'interface de l'utilisateur. CustomView Je pense que la hauteur de la CustomView est déterminée par les contraintes sur l'interface de l'utilisateur. UILabel "titre" dans celui-ci.

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 le haut du UITableView et ils se déplacent ensemble lorsque je fais défiler.

0voto

Je sais qu'il s'agit d'un vieux message, mais après avoir parcouru tous les messages de l'OS à ce sujet et passé une après-midi entière à jouer avec, j'ai finalement trouvé une solution propre et très simple.

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

  1. Vue de la table
    1. Voir tableHeaderView
      1. Voir avec un point de vente appelé headerView

Maintenant, à l'intérieur de la vue (n°3), je configure toutes les contraintes comme je le ferais normalement, y compris l'espace inférieur du conteneur. Cela permettra au conteneur (c'est-à-dire la vue n°3, c'est-à-dire la vue d'en-tête) de se dimensionner en fonction de ses sous-vues et de leurs contraintes.

Après cela, j'ai fixé les contraintes entre 3. View y 2. View à ceux-là :

  1. Espace supérieur au conteneur : 0
  2. Espace avant le conteneur : 0
  3. Espace à la fin du conteneur : 0

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

Une fois que tout cela est fait dans le storyboard, il ne reste plus qu'à 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;
}

0voto

user1511613 Points 13

Conseils : Si vous utilisez la méthode setAndLayoutTableHeaderView, vous devez mettre à jour le cadre des sous-vues, donc dans cette situation la preferredMaxLayoutWidth de UILabel doit être appelée avant l'appel de systemLayoutSizeFittingSize, ne pas appeler dans layoutSubview.

code show

0voto

Vincent Points 261

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

Utilisation.

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

0voto

Un vieux post. Mais un bon message. Voici mes deux cents.

Tout d'abord, assurez-vous que les contraintes de votre vue d'en-tête sont disposées de manière à ce qu'elle puisse supporter la taille intrinsèque de son contenu. 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)
}

0voto

Eugene Dubinin Points 106

J'y suis parvenu grâce à l'approche suivante (cela fonctionne de la même manière pour le pied de page).

D'abord, vous aurez besoin de petits UITableView extension :

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 l'implémentation de votre classe de contrôleur de vue :

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

Notes sur un en-tête UIView de l'implémentation de la vue secondaire :

  1. Vous devez vous assurer à 100% que votre vue d'en-tête a la bonne configuration d'autolayout. Je vous recommande de commencer par une vue d'en-tête simple avec une seule contrainte de hauteur et d'essayer la configuration ci-dessus.

  2. Remplacer requiresConstraintBasedLayout et retourner true :

.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X