web-dev-qa-db-fra.com

'NSInvalidArgumentException', raison: 'Impossible d'analyser le format de contrainte'

J'ai une sous-vue que je souhaite conserver lors de la rotation de l'écran, j'ai donc décidé de mettre le type NSLayoutConstraint:

Trailing Space to Superview
Top Space to Superview
Espace des boutons vers Superview
Je fais partie d'une sous-classe de UITableViewCell. J'ai écrit le code mais j'obtiens l'erreur suivante:

'NSInvalidArgumentException', reason: 'Unable to parse constraint format: 
 self is not a key in the views dictionary. 
 H:[self.arrows]-5-|


Mon code dans CustomCell.m est:

 self.arrows = [[Arrows alloc]initWithFrame:CGRectMake(self.contentView.bounds.size.width-30, self.bounds.Origin.y+4, 30, self.contentView.bounds.size.height-4)];

 NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(self.arrows, self.contentView);
 NSMutableArray * constraint=[[NSMutableArray alloc]init];
 [constraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:  [self.arrows]-5-|" options:0 metrics:nil views:viewsDictionary]];
 [constraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-1-[self.arrows]" options:0 metrics:nil views:viewsDictionary]];
 [constraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"[V: [self.arrows]-1-|" options:0 metrics:nil views:viewsDictionary]];
 [self.arrows addConstraints:constraint];
50
user1747321

Il semble que le moteur d'analyse au format visuel de la mise en page automatique interprète le "." Dans votre contrainte VFL comme étant un chemin de clé au lieu d'une clé comme celle qui utilise valueForKeyPath:.

NSDictionaryOfVariableBindings(...) prendra tout ce que votre paramètre est entre parenthèses et le traduira en une clé littérale avec l'objet comme valeur (dans votre cas: @{"self.arrow" : self.arrow}). Dans le cas du VFL, la mise en page automatique pense que vous avez une clé nommée self dans votre dictionnaire de vues avec un sous-domaine (ou sous-objet) qui a une clé de arrow,

@{
   @"self" : @{ @"arrow" : self.arrow }
}

lorsque vous vouliez littéralement que le système interprète votre clé comme "self.arrow".

Habituellement, lorsque j'utilise un getter de variables d'instance comme celui-ci, je finis généralement par créer mon propre dictionnaire au lieu d'utiliser NSDictionaryOfVariableBindings(...) comme ceci:

NSDictionary *views = @{ @"arrowView" : self.arrow }

ou

NSDictionary *views = NSDictionaryOfVariableBindings(_arrow);

Ce qui vous permettrait d'utiliser la vue dans votre VFL sans vous-même et vous savez toujours de quoi vous parlez:

NSArray *arrowHorizConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[arrowView]-5-|" options:0 metrics:nil views];

ou

NSArray *arrowHorizConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[_arrow]-5-|" options:0 metrics:nil views];

En règle générale, j'ai appris à ne pas avoir de clés de dictionnaire avec un point (.) Pour éviter toute confusion système ou déboguer des cauchemars.

121
larsacus

Mon astuce consiste à simplement déclarer une variable locale qui n'est qu'un autre pointeur vers la propriété et à la placer dans le NSDictionaryOfVariableBindings.

@interface ViewController ()
@property (strong) UIButton *myButton;
@property (strong) UILabel *myLabel;
@end

...

UIButton *myButtonP = self.myButton;
UILabel *theLabelP = self.myLabel;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(myButtonP, myLabelP);

Le suffixe P est pour "pointeur".

7
Eric

La solution la plus simple consiste à éviter les getters pour les variables de votre propre classe et à redéfinir les variables des superclasses en tant que variables locales. Une solution pour votre exemple est

UIView *contentView = self.contentView;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_arrows, contentView);
1
Mihai Damian