web-dev-qa-db-fra.com

Problème avec la mise en page automatique sur UITableViewCell

J'ai des problèmes avec la mise en page automatique sur un xcode 5 projet. J'utilise un contrôleur de vue simple à l'intérieur avec un contrôleur de navigation. J'ai un MKMapView sur la moitié supérieure et un UITableView sur la moitié inférieure. J'utilise storyboards et j'ai configuré le prototype UITableViewCell, mais j'ajoute les contraintes via du code. J'ai revérifié chaque contrôle dans le prototype et je ne vois aucune contrainte configurée là-bas. Mon problème se produit lorsque j'ajoute les contraintes pour le UITableViewCell. J'ai le code suivant dans les cellules:

-(void)updateConstraints {
    [super updateConstraints];
    //first remove old constraints
    [self removeConstraints:self.constraints];
    [self.nameLabel removeConstraints:self.nameLabel.constraints];
    [self.addressLabel removeConstraints:self.nameLabel.constraints];
    [self.rentableSquareFeetLabel removeConstraints:self.rentableSquareFeetLabel.constraints];
    [self.lastSaleAmountLabel removeConstraints:self.lastSaleAmountLabel.constraints];
    [self.lastSaleDateLabel removeConstraints:self.lastSaleAmountLabel.constraints];
    [self.thumbnailImageView removeConstraints:self.thumbnailImageView.constraints];

    //then set up constraints
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_thumbnailImageView, _nameLabel, _rentableSquareFeetLabel, _lastSaleAmountLabel, _addressLabel, _lastSaleDateLabel);
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_thumbnailImageView(60)]-[_nameLabel(<=200)]-(>=8)-[_rentableSquareFeetLabel]-(>=8)-[_lastSaleAmountLabel]|" options:0 metrics:nil views:viewsDictionary]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-(-4)-[_addressLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_lastSaleAmountLabel]-(-4)-[_lastSaleDateLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];
}

J'obtiens ce qui suit dans la console de débogage. L'exception est déclenchée par la première ligne addConstraints. Si je continue juste à travers ceux-ci, finalement tout apparaît comme il se doit, car il semble que xcode choisisse de briser la bonne contrainte:

2013-09-25 15:07:14.169 PECProperties[32381:a0b] Unable to simultaneously satisfy constraints.  Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)  (
    "<NSIBPrototypingLayoutConstraint:0x9d56c70 'IB auto generated at build time for view with fixed frame' H:|-(0)-[UIImageView:0x9d558f0](LTR)   (Names: '|':UITableViewCellContentView:0x9d55620 )>",
    "<NSIBPrototypingLayoutConstraint:0x9d56d20 'IB auto generated at build time for view with fixed frame' H:[UIImageView:0x9d558f0(60)]>",
    "<NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR)   (Names: '|':UITableViewCellContentView:0x9d55620 )>",
    "<NSLayoutConstraint:0x9d53830 H:[UIImageView:0x9d558f0]-(NSSpace(8))-[UILabel:0x9d559e0]>" )

Will attempt to recover by breaking constraint  <NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR)   (Names: '|':UITableViewCellContentView:0x9d55620 )>

Break on objc_exception_throw to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Le troisième NSIBPrototypingLayoutConstraint montre 78 points entre le bord de la vue et une étiquette. C'est là que le prototype est positionné à peu près (et si je le déplace dans le prototype, je vois le changement dans la contrainte dans la console de débogage), mais cela entre en conflit avec ma propre contrainte de distance "standard" entre la vue de l'image et l'étiquette .

J'ai essayé de régler le translatesAutoresizingMaskIntoConstraints=NO dans le contrôleur de vue cellForRowAtIndexPath, mais cela ne semble pas non plus aider. Comment puis-je corriger la mise en page?

33
Stephen Moore

Quelques points à couvrir ici:

  1. Les contraintes NSIBPrototypingLayoutConstraint que vous rencontrez (et qui provoquent des exceptions) sont générées automatiquement par Interface Builder afin de rendre votre disposition de vue Storyboard ou XIB non ambiguë. C'est assez sournois de le faire, mais cela ajoute automatiquement les contraintes minimales requises pour que la position et la taille de chaque vue ambiguë soient entièrement spécifiées. Il s'agit d'un changement par rapport à Xcode 4, car dans Xcode 4, vous ne pouviez pas avoir de dispositions ambiguës dans Interface Builder. Avec Xcode 5 et versions ultérieures, vous pouvez, cependant, IB générera automatiquement ces contraintes pour vous si votre mise en page est ambiguë au moment de la compilation.

    La façon de résoudre ce problème consiste à ajouter les contraintes minimales requises dans Interface Builder afin que la position et la taille de chaque vue soient entièrement spécifiées, puis sélectionnez chacune de ces contraintes indésirables, accédez à l'inspecteur des attributs de la barre latérale droite et cochez la case en regard de Espace réservé - Supprimer au moment de la construction .

    Screenshot of the constraint Placeholder checkbox in Xcode 6.

    Non seulement cette case à cocher supprime la contrainte que vous avez ajoutée, mais surtout elle empêchera la contrainte IB générée automatiquement de prendre sa place! (Comme vous pouvez l'imaginer, cela est assez fastidieux lorsque vous avez un certain nombre de vues dans IB et que vous souhaitez gérer toutes vos contraintes dans le code. Pour cette raison, vous souhaiterez peut-être éviter d'utiliser IB entièrement pour les hiérarchies de vues dans lesquelles vous avez l'intention d'implémenter Auto. Mise en page par programmation.)

    Quelle est la différence entre une contrainte d'espace réservé et une contrainte non installée? Voici une diapositive de mon Présentation de la mise en page automatique adaptative (vidéo) ( diapositives PDF ) comparant les deux:

    Comparison between Placeholder constraints and Uninstalled constraints.

  2. Dans updateConstraints, vous ne voulez pas supprimer les contraintes et les rajouter comme vous l'avez fait là-bas. Pourquoi pas? Essentiellement, c'est terrible pour les performances, et j'ai confirmé avec Apple ingénieurs que ce n'est pas une bonne idée. Voir la question/réponse que j'ai postée ici pour en savoir plus détails, ainsi que ceci réponse . Afin d'éviter l'ajout de contraintes plus d'une fois, utilisez un indicateur booléen (par exemple hasSetupConstraints) que vous définissez sur OUI une fois que vous avez configuré votre contraintes la première fois, et si updateConstraints est appelé à nouveau, vous pouvez simplement retourner immédiatement si vous n'avez pas de nouvelles contraintes à ajouter. Voir cette question pour plus de détails.

  3. Le code que vous utilisez pour supprimer les contraintes peut ne pas fonctionner complètement. Ceci est dû au fait [view removeConstraints:view.constraints] supprimera uniquement les contraintes qui ont été ajoutées à view - rappelez-vous que les contraintes peuvent être ajoutées à toute vue d'ensemble commune des vues qu'elles contraignent - et les contraintes ajoutées à view peuvent ne pas être les seuls affectant la disposition de view! Si vous devez supprimer un certain nombre de contraintes, vous devez stocker une référence à chacune de ces contraintes dans une propriété (par exemple, une propriété NSArray contenant des instances NSLayoutConstraint), puis désactiver/supprimer ces contraintes à l'aide de l'API sur NSLayoutConstraint ou la bibliothèque open source PureLayout . Vous ne devez désactiver/supprimer que le moins de contraintes possible car cela est coûteux en termes de calcul. D'un autre côté, changer la constant de n'importe quelle contrainte est très efficace et encouragé, et vous n'avez pas besoin de supprimer ou de rajouter la contrainte pour ce faire.

79
smileyborg