web-dev-qa-db-fra.com

Création de contraintes de présentation par programme

Je sais que beaucoup de gens ont déjà posé des tonnes de questions à ce sujet, mais même avec les réponses, je ne peux pas le faire fonctionner.

Lorsque je suis confronté à des contraintes sur le scénarimage, c'est facile, mais en code, j'ai du mal. J'essaie, par exemple, d'avoir une vue qui reste sur le côté droit et a la hauteur de l'écran en fonction de l'orientation de l'écran. Ceci est mon code:

UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 748)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:0 metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Cela ne satisfait pas certaines contraintes. Je ne vois pas ce qui ne va pas. Aussi, pourquoi ne puis-je pas utiliser une propriété comme self.myView au lieu d'une variable locale telle que myView?

84
pierre23

Lorsque vous utilisez la disposition automatique dans le code, définir le cadre ne fait rien. Ainsi, le fait que vous ayez spécifié une largeur de 200 sur la vue ci-dessus ne signifie rien lorsque vous définissez des contraintes sur celle-ci. Pour qu'un ensemble de contraintes de vue soit non ambigu, il nécessite quatre éléments: une position x, une position y, une largeur et une hauteur pour tout état donné.

Actuellement, dans le code ci-dessus, vous n'en avez que deux (hauteur, par rapport à l'aperçu et position y, par rapport à l'aperçu). En plus de cela, vous avez deux contraintes requises qui peuvent être en conflit en fonction de la configuration des contraintes de la vue supérieure. Si la vue d'ensemble devait comporter une contrainte requise spécifiant que la hauteur de celle-ci serait inférieure à 748, vous obtiendrez une exception "contraintes non satisfaisantes".

Le fait que vous ayez défini la largeur de la vue avant de définir des contraintes ne signifie rien. Il ne tiendra même pas compte de l’ancien cadre et calculera un nouveau cadre en fonction de toutes les contraintes spécifiées pour ces vues. Lorsque je traite avec l'autolayout en code, je crée généralement une nouvelle vue en utilisant initWithFrame:CGRectZero ou simplement init.

Pour créer le jeu de contraintes requis pour la mise en page que vous avez décrite verbalement dans votre question, vous devez ajouter des contraintes horizontales pour délimiter la largeur et la position x afin de donner une mise en page entièrement spécifiée:

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"H:[myView(==200)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Décrire verbalement cette mise en page se lit comme suit en commençant par la contrainte verticale:

myView remplira la hauteur de sa vue avec un remplissage supérieur et inférieur égal à l'espace standard. La vue d'ensemble de myView a une hauteur minimale de 748pts. La largeur de myView est de 200 pts et son rembourrage à droite est égal à l'espace standard par rapport à sa vue d'ensemble.

Si vous souhaitez simplement que la vue remplisse toute la hauteur de la vue supérieure sans restreindre celle-ci, vous devez simplement omettre le paramètre (>=748) dans le texte du format visuel. Si vous pensez que le paramètre (>=748) est requis pour lui donner une hauteur, vous ne devez pas dans ce cas: épingler la vue aux bords de la vue d'ensemble à l'aide de la barre (|) ou de la barre d'espacement (|-, -|) syntaxe, vous donnez à votre vue une position y (épinglant la vue sur un seul bord) et une position y avec une hauteur (épinglant la vue sur les deux bords), satisfaire votre contrainte définie pour la vue.

En ce qui concerne votre deuxième question:

Si vous utilisez NSDictionaryOfVariableBindings(self.myView) (si vous avez défini une propriété pour myView) et que vous l'introduisez dans votre VFL afin d'utiliser self.myView dans votre texte VFL, vous obtiendrez probablement une exception lorsque autolayout tentera d'analyser votre texte VFL. Cela concerne la notation par points dans les clés du dictionnaire et le système essayant d'utiliser valueForKeyPath:. Voir ici pour une question similaire et réponse .

106
larsacus

Bonjour, je me sers beaucoup de cette page pour les contraintes et "comment faire". Il m'a fallu une éternité pour réaliser que j'avais besoin de:

myView.translatesAutoresizingMaskIntoConstraints = NO;

pour que cet exemple fonctionne. Merci Userxxx, Rob M. et surtout larsacus pour l'explication et le code ici, cela a été inestimable.

Voici le code complet pour obtenir les exemples ci-dessus à exécuter:

UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
myView.translatesAutoresizingMaskIntoConstraints = NO;  //This part hung me up 
[self.view addSubview:myView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[myView(>=200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:[myView(==200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];
61
BriOnH

Version rapide

mis à jour pour Swift 3

Cet exemple montrera deux méthodes pour ajouter par programme les contraintes suivantes de la même façon que si vous le faisiez dans le Générateur d'interface:

Largeur et hauteur

enter image description here

Centre dans le conteneur

enter image description here

Code de la plaque de cuisson

override func viewDidLoad() {
    super.viewDidLoad()

    // set up the view
    let myView = UIView()
    myView.backgroundColor = UIColor.blue
    myView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(myView)

    // Add constraints code here (choose one of the methods below)
    // ...
}

Méthode 1: style d'ancrage

// width and height
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true

// center in container
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

Méthode 2: style NSLayoutConstraint

// width and height
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true

// center in container
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true

Remarques

  • Le style d'ancrage est la méthode préférée par rapport à NSLayoutConstraint Style. Toutefois, il n'est disponible que sur iOS 9. Par conséquent, si vous prenez iOS 8 en charge, vous devez toujours utiliser NSLayoutConstraint Style.
  • Voir aussi la documentation Création par programme de contraintes .
  • Voir cette réponse pour un exemple similaire d'ajout d'une contrainte d'épinglage.
10
Suragch

Concernant votre deuxième question sur les propriétés, vous pouvez utiliser self.myView uniquement si vous l'avez déclaré en tant que propriété de la classe. Comme myView est une variable locale, vous ne pouvez pas l'utiliser de cette façon. Pour plus de détails à ce sujet, je vous recommanderais de consulter la documentation Apple sur Propriétés déclarées ,

5
iDev

pourquoi ne puis-je pas utiliser une propriété comme self.myView au lieu d'une variable locale telle que myView?

essayez d'utiliser:

NSDictionaryOfVariableBindings(_view)

au lieu de self.view

5
Megarushing

Veuillez également noter que depuis iOS9, vous pouvez définir des contraintes par programmation "plus concises et plus faciles à lire" à l'aide de sous-classes de la nouvelle classe d'assistance NSLayoutAnchor .

Un exemple tiré de la doc:

[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor constant: 8.0].active = true;
4
andreacipriani