Implémentation programmatique de deux mises en page différentes en utilisant des classes de taille

J'ai une disposition de quatre boutons. En portrait, ils devraient être montrés l'un au-dessus de l'autre. Dans le paysage, ils devraient être en deux colonnes chacune avec deux boutons.

J'implémente les boutons dans le code-des choses vraiment simples:

UIButton *btn1 = [[UIButton alloc] init];
[self.view addSubview: btn1]; 

UIButton *btn2 = [[UIButton alloc] init];
[self.view addSubview: btn2]; 

UIButton *btn3 = [[UIButton alloc] init];
[self.view addSubview: btn3]; 

UIButton *btn4 = [[UIButton alloc] init];
[self.view addSubview: btn4]; 

NSDictionary *views = NSDictionaryOfVariableBindings(btn1, btn2, btn3, btn4);

[btn1 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn2 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn3 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn4 setTranslatesAutoresizingMaskIntoConstraints:NO];

// portrait constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn1]-(50)-|"
                                                                 options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn2]-(50)-|"
                                                                 options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn3]-(50)-|"
                                                                 options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn4]-(50)-|"
                                                                 options:0 metrics:nil views:views]];

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn1]-[btn2]-[btn3]-[btn4]-(50)-|"
                                                                 options:0 metrics:nil views:views]];

C'est évidemment la configuration pour la mise en page portrait. J'aurais l'habitude d'avoir déterminé l'appareil et son orientation pour faire un cas spécifique pour iPad et iPhone dans les orientations respectives. Mais maintenant, nous sommes censés utiliser des classes de taille. Comment puis-je déterminer si la classe de taille est "compacte"... et donc définir les contraintes appropriées?

32
demandé sur Suragch 2014-09-05 16:22:53

4 réponses

Vous pouvez examiner la collection de traits de la vue pour déterminer sa classe de taille horizontale et verticale.

if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    ...
}

Implémentez la méthode traitCollectionDidChange: pour être appelée automatiquement lorsqu'un trait change en raison de l'autorotation.

Pour plus d'informations, consultez référence de classe UITraitCollection et référence de protocole Uitraitenvironment.

33
répondu bcattle 2016-08-10 04:37:39

En attendant, j'ai trouvé une bonne solution. Puisque cette question a tellement de upvotes, j'ai pensé que je la décrirais rapidement. J'ai été inspiré à cette solution par une session WWDC.

Je suis passé à Swift alors veuillez excuser que le code sera dans swift - le concept est cependant le même pour Obj-C.

Vous commencez par déclarer trois tableaux de contraintes:

 // Constraints
 private var compactConstraints: [NSLayoutConstraint] = []
 private var regularConstraints: [NSLayoutConstraint] = []
 private var sharedConstraints: [NSLayoutConstraint] = []

Et puis vous remplissez les contraintes en conséquence. Vous pouvez IE faire ceci dans une fonction distincte que vous appel de viewDidLoad, ou vous le faites dans viewDidLoad directement.

sharedConstraints.append(contentsOf: [
     btnStackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
    ...
])

compactConstraints.append(contentsOf: [
     btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7),
    ...
])

regularConstraints.append(contentsOf: [
     btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.4),
    ...
])

La partie importante est de basculer entre les classes de taille et d'activer / désactiver les contraintes appropriées.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

    super.traitCollectionDidChange(previousTraitCollection)

    if (!sharedConstraints[0].isActive) {
       // activating shared constraints
       NSLayoutConstraint.activate(sharedConstraints)
    }


    if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
        if regularConstraints.count > 0 && regularConstraints[0].isActive {
            NSLayoutConstraint.deactivate(regularConstraints)
        }
        // activating compact constraints
        NSLayoutConstraint.activate(compactConstraints)
    } else {
        if compactConstraints.count > 0 && compactConstraints[0].isActive {
            NSLayoutConstraint.deactivate(compactConstraints)
        }
        // activating regular constraints
        NSLayoutConstraint.activate(regularConstraints)
    }
}

Je sais que les contraintes ne correspondent pas à celles de la question. Mais les contraintes elles-mêmes ne sont pas pertinentes. L'essentiel est de savoir comment basculer entre deux ensembles de contraintes en fonction de la classe de taille.

J'espère que cela aide.

26
répondu Joseph 2017-01-23 16:46:58

Code Swift 4 pour la réponse acceptée:

if (self.view.traitCollection.horizontalSizeClass == .compact) {
...
}
3
répondu RemyDCF 2017-10-10 20:11:23

TraitCollection ne fonctionne que dans iOS8. Donc, votre application va planter sur iOS7. Utilisez le code ci-dessous pour prendre en charge iOS7 et iOS8

if ([self.view respondsToSelector:@selector(traitCollection)]){

    if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    ...
    }

}
1
répondu Kirit Vaghela 2016-08-22 02:58:18