web-dev-qa-db-fra.com

Qu'est-ce que NSLayoutConstraint "UIView-Encapsulated-Layout-Height-Height" et comment dois-je m'y prendre pour le forcer à se recalculer proprement?

J'ai un UITableView sous iOS 8 et j'utilise les hauteurs de cellule automatiques à partir des contraintes d'un storyboard.

Une de mes cellules contient un seul UITextView et j'en ai besoin pour se contracter et se développer en fonction des entrées de l'utilisateur - appuyez sur pour réduire/agrandir le texte.

Pour ce faire, j'ajoute une contrainte d'exécution à la vue texte et modifie la constante de la contrainte en réponse aux événements de l'utilisateur:

-(void)collapse:(BOOL)collapse; {

    _collapsed = collapse;

    if(collapse)
            [_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
        else
            [_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];

    [self setNeedsUpdateConstraints];

}

Chaque fois que je fais cela, je l'enveloppe dans tableView mises à jour et appelle [tableView setNeedsUpdateConstraints]:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView setNeedsUpdateConstraints];
// I have also tried 
// [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// with exactly the same results.

[tableView endUpdates];

Lorsque je le fais, ma cellule s’agrandit (et s’anime en même temps) mais un avertissement de contrainte s’affiche:

2014-07-31 13:29:51.792 OneFlatEarth[5505:730175] 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) 

(

    "<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>",

    "<NSLayoutConstraint:0x7f94dced2260 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']-(15)-|   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced2350 V:|-(6)-[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced6480 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f94de5773a0(91)]>"



 )

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>

388 est ma hauteur calculée, les autres contraintes sur la UITextView sont les miennes de Xcode/IB.

Le dernier me dérange - je suppose que UIView-Encapsulated-Layout-Height est la hauteur calculée de la cellule lors du premier rendu - (je règle ma UITextView hauteur sur> = 70.0) mais elle ne le fait pas. Il semble bien que cette contrainte dérogée remplace alors un utilisateur mis à jour, cnstraint.

Pire, bien que le code de disposition indique qu'il essaie de casser ma contrainte de hauteur, il ne le fait pas - il recalcule ensuite la hauteur de la cellule et tout s'affiche à ma guise.

Alors, qu'est-ce que NSLayoutConstraintUIView-Encapsulated-Layout-Height (je suppose que c'est la hauteur calculée pour le dimensionnement automatique des cellules) et comment dois-je m'y prendre pour le forcer à recalculer proprement?

233
Rog

Essayez de réduire la priorité de votre _collapsedtextHeightConstraint à 999. De cette manière, le système fourni par la contrainte UIView-Encapsulated-Layout-Height est toujours prioritaire.

Il est basé sur ce que vous retournez dans -tableView:heightForRowAtIndexPath:. Assurez-vous de renvoyer la bonne valeur et votre propre contrainte et celle générée doit être la même. La priorité la plus basse pour votre propre contrainte n’est nécessaire que temporairement pour éviter les conflits lorsque les animations de réduction/développement sont en vol.

283
Ortwin Gentz

J'ai un scénario similaire: une vue sous forme de tableau avec une cellule d'une ligne, dans laquelle il y a quelques lignes d'objets UILabel. J'utilise iOS 8 et l'autolayout.

Lorsque j'ai effectué ma rotation, la mauvaise hauteur de ligne calculée par le système était erronée (43,5 est bien inférieur à la hauteur réelle). On dirait:

"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"

Ce n'est pas juste un avertissement. La disposition de ma cellule de la vue tableau est terrible: tout le texte est superposé sur une ligne de texte.

Cela me surprend que la ligne suivante "corrige" mon problème comme par magie (l'autolayout ne se plaint de rien et j'obtiens ce que j'attends à l'écran):

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works

avec ou sans cette ligne:

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem
62
Donald

J'ai pu obtenir que l'avertissement s'en aille en spécifiant une priorité sur l'une des valeurs de la contrainte, les messages d'avertissement indiquent qu'il devait se rompre (sous "Will attempt to recover by breaking constraint"). Il semble que tant que je mets la priorité sur quelque chose de plus grand que 49, l'avertissement disparaît.

Pour moi, cela signifiait changer de contrainte. L'avertissement disait qu'il tentait de casser:

@"V:|[contentLabel]-[quoteeLabel]|"

à:

@"V:|-0@500-[contentLabel]-[quoteeLabel]|"

En fait, je peux ajouter une priorité à l’un des éléments de cette contrainte et cela fonctionnera. Cela ne semble pas avoir d'importance. Mes cellules se retrouvent à la bonne hauteur et l'avertissement ne s'affiche pas. Roger, pour votre exemple, essayez d'ajouter @500 juste après la contrainte 388 height (par exemple 388@500).

Je ne suis pas tout à fait sûr pourquoi cela fonctionne mais j'ai fait une petite enquête. Dans le NSLayoutPriority enum , il apparaît que le niveau de priorité NSLayoutPriorityFittingSizeCompression est 50. La documentation pour ce niveau de priorité indique:

Lorsque vous envoyez un message fitSize à une vue, la plus petite taille suffisante pour le contenu de la vue est calculée. C'est le niveau de priorité avec lequel la vue veut être aussi petite que possible dans ce calcul. C'est assez bas. Il n’est généralement pas approprié de créer une contrainte exactement à cette priorité. Vous voulez être plus haut ou plus bas.

Le message documentation du message fittingSize référencé se lit comme suit:

La taille minimale de la vue qui satisfait les contraintes qu'elle contient. (lecture seulement)

AppKit définit cette propriété sur la taille la plus adaptée à la vue, en tenant compte de toutes les contraintes que cette dernière et ses sous-vues tiennent, et en satisfaisant une préférence pour rendre la vue aussi petite que possible. Les valeurs de taille dans cette propriété ne sont jamais négatives.

Je n'ai pas creusé plus loin que ça, mais il semble logique que cela ait un lien avec le problème.

31
Jeff Bowen

J'ai pu résoudre cette erreur en supprimant une cell.layoutIfNeeded() fausse que j'avais dans ma méthode tableView's cellForRowAt.

11
Clifton Labrum

Au lieu d’informer la vue tabulaire pour mettre à jour ses contraintes, essayez de recharger la cellule:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

UIView-Encapsulated-Layout-Height est probablement la hauteur de la vue tabulaire calculée pour la cellule lors du chargement initial, en fonction des contraintes de la cellule à ce moment-là.

6
Austin

Une autre possibilité:

Si vous utilisez la mise en page automatique pour calculer la hauteur de la cellule (hauteur de contentView, la plupart du temps comme ci-dessous), et si vous avez le séparateur uitableview, vous devez ajouter la hauteur du séparateur afin de renvoyer la hauteur de la cellule. Une fois que vous aurez la bonne hauteur, vous ne recevrez plus cet avertissement.

- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
   [sizingCell setNeedsLayout];
   [sizingCell layoutIfNeeded];
   CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
   return size.height; // should + 1 here if my uitableviewseparatorstyle is not none

}
5
Cullen SUN

Comme mentionné par Jesse dans le commentaire de la question, cela fonctionne pour moi:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

Pour votre information, ce problème ne se produit pas dans iOS 10.

3
Phu Nguyen

J'ai eu cette erreur lors de l'utilisation de UITableViewAutomaticDimension et de la modification d'une contrainte de hauteur sur une vue à l'intérieur de la cellule.

J'ai finalement compris que c'était dû au fait que la constante de contrainte n'était pas arrondie au nombre entier le plus proche.

let neededHeight = width / ratio // This is a CGFloat like 133.2353
constraintPictureHeight.constant = neededHeight // Causes constraint error
constraintPictureHeight.constant = ceil(neededHeight) // All good!
2
Che

Après avoir passé quelques heures à me gratter la tête avec ce bogue, j'ai finalement trouvé une solution qui fonctionnait pour moi. Mon problème principal était que j'avais plusieurs plumes enregistrées pour différents types de cellules, mais qu'un seul type de cellule pouvait spécifiquement avoir des tailles différentes (toutes les instances de cette cellule n'auront pas la même taille). le problème est donc survenu lorsque la table a essayé de retirer une cellule de ce type et que sa hauteur était différente. Je l'ai résolu en mettant

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

chaque fois que la cellule avait ses données pour calculer sa taille. Je pense que ça peut être dans

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

quelque chose comme

cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

J'espère que cela t'aides!

1

Le dimensionnement de la vue texte pour s’adapter à son contenu et la mise à jour de la constante de contrainte de hauteur à la hauteur résultante ont corrigé le conflit de contraintes UIView-Encapsulated-Layout-Height, par exemple:

[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;
1
Kim André Sand

Je recevais un message comme celui-ci:

Impossible de satisfaire simultanément aux contraintes ...
...
...
...
NSLayoutConstraint: 0x7fe74bdf7e50 'UIView-Encapsulated-Layout-Layout-Height' V: [UITableViewCellContentView: 0x7fe75330c5c0 (21.5)]
...
...
Tentera de récupérer en rompant la contrainte NSLayoutConstraint: 0x7fe0f9b200c0 UITableViewCellContentView: 0x7fe0f9b1e090.bottomMargin == UILabel: 0x7fe0f9b1e970.bottom

J'utilise un UITableViewCell personnalisé avec UITableViewAutomaticDimension pour la hauteur. Et j'ai aussi implémenté la méthode estimatedHeightForRowAtIndex:.

La contrainte qui me posait des problèmes ressemblait à ceci

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];

Changer la contrainte à ceci résoudra le problème, mais comme pour une autre réponse, j’ai estimé que ce n’était pas correct, car cela réduisait la priorité d’une contrainte que je souhaitais demander:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6@999-[title]-6@999-|" options:0 metrics:nil views:views];

Cependant, ce que j'ai remarqué, c'est que si je supprime simplement la priorité, cela fonctionne également et je ne reçois pas les journaux de contraintes de rupture:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[title]-6-|" options:0 metrics:nil views:views];

Ceci est un peu mystérieux quant à la différence entre |-6-[title]-6-| et |-[title-|. Mais spécifier la taille ne me pose pas de problème, cela supprime les journaux et je n'ai pas besoin de réduire la priorité de mes contraintes requises.

0
user1687195

TableView obtient la hauteur de la cellule à la propriété indexPath du délégué. puis obtenez la cellule de cellForRowAtIndexPath:

top (10@1000)
    cell
bottom (0@1000)

si cell.contentView.height: 0 // <-> (UIView-Encapsulated-Layout-Height: 0 @ 1000) en haut (10 @ 1000) en conflit avec (UIView-Encapsulated-Layout-Height: 0 @ 1000),

à cause d’eux, la priorité est égale à 1000. Nous devons définir la priorité la plus élevée sous la priorité de UIView-Encapsulated-Layout-Height.

0
user2962814