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.

153voto

Ben Packard Points 3441

J'ai posé et répondu à une question similaire aquí . En résumé, j'ajoute l'en-tête une fois et je l'utilise pour trouver la hauteur requise. Cette hauteur peut ensuite être appliquée à l'en-tête, et l'en-tête 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 à plusieurs lignes, cela dépend également de la vue personnalisée qui définit la largeur maximale de mise en page préférée 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 de janvier 2015

Malheureusement, cela semble encore nécessaire. Voici une version rapide 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 cela dans une extension de 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
        self.tableHeaderView?.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            header.widthAnchor.constraint(equalTo: self.widthAnchor)
        ])
        header.setNeedsLayout()
        header.layoutIfNeeded()
        header.frame.size =  header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        self.tableHeaderView = header
    }
}

Utilisation :

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

26voto

rdelmar Points 53270

Je n'ai pas réussi à ajouter une vue d'en-tête en utilisant des contraintes (dans le code). Si je donne à ma vue une contrainte de largeur et/ou de hauteur, j'obtiens un crash avec le message suivant :

 "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 de table, elle ne présente aucune contrainte et fonctionne bien en tant que vue d'en-tête. Je pense donc que le placement de la vue d'en-tête n'est pas effectué à l'aide de contraintes. Je pense donc que le placement de la vue d'en-tête ne se fait pas à l'aide de contraintes. Elle ne semble pas se comporter comme une vue normale à cet égard.

La largeur est automatiquement la largeur de la vue de la table, la seule chose que vous devez définir est la hauteur - les valeurs d'origine sont ignorées, donc peu importe ce que vous mettez pour celles-ci. Par exemple, ceci a bien fonctionné (tout comme 0,0,0,80 pour le rectangle) :

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

19voto

Ramon Vasconcelos Points 952

J'ai vu beaucoup de méthodes ici qui font beaucoup de choses inutiles, mais vous n'avez pas besoin de tant de choses pour utiliser la mise en page automatique dans la vue d'en-tête. Il 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
    }

7voto

Martin Points 3170

Une autre solution consiste à dispatcher 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;
    });
}

Note : Il corrige le bug lorsque la vue chargée a une hauteur fixe. Je n'ai pas essayé lorsque la hauteur de l'en-tête ne dépend que de son contenu.

EDIT :

Vous pouvez trouver une solution plus propre à ce problème en mettant en œuvre la méthode suivante fonction et l'appeler dans viewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}

5voto

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

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

Le code en 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 requiresConstraintBasedLayout dans votre sous-classe entraîne l'exécution d'un passage de mise en page, mais les résultats de ce passage sont ignorés et le cadre du système est fixé à la largeur du tableView et à la hauteur 0.

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