Bonjour, Habr! Je vous présente la traduction de l'article
Création de contraintes UIViews par
programme à l'aide de PureLayout par Aly Yaka.

Aujourd'hui, je vais vous guider à travers la création d'une interface utilisateur d'application mobile simple avec du code, sans utiliser de storyboards ou de NIB. Je n'entrerai pas dans les discussions sur ce qui est le mieux, car tout a ses avantages et ses inconvénients, je
vais donc simplement
laisser un lien qui va dans cette affaire .
Dans la deuxième partie de ce guide, nous allons créer certains des éléments d'interface utilisateur d'application mobile les plus couramment utilisés avec du code, y compris une barre de navigation, une vue tabulaire et des cellules de taille dynamique.
Revue
Ce tutoriel a été écrit en utilisant Xcode 9 et Swift 4. Je suppose également que vous connaissez bien Xcode, Swift et CocoaPods.
Sans plus tarder, commençons à créer notre projet: une simple demande de carte de contact. Le but de cet article est de vous apprendre à créer l'interface utilisateur de votre application en code et, par conséquent, il ne contiendra aucune logique concernant la fonctionnalité de l'application, sauf si cela est nécessaire aux fins de ce guide.
Création de contraintes par programme avec PureLayout
Configuration du projet
Commencez par lancer Xcode -> "Créer un nouveau projet Xcode". Sélectionnez «Single View App» et cliquez sur «Suivant».

Nommez le projet comme vous le souhaitez, j'ai décidé de l'appeler ContactCard. Désactivez les trois options ci-dessous et sélectionnez Swift comme langage de programmation, puis cliquez sur Suivant.

Sélectionnez un emplacement sur votre ordinateur pour enregistrer le projet. Décochez "Créer un référentiel Git sur mon Mac".
Étant donné que nous n'utiliserons pas de storyboards ou de NIB dans ce projet, supprimez le «Main.storyboard», qui se trouve dans Project Navigator:

Après cela, cliquez sur le projet dans le navigateur de projet et sur l'onglet «Général», recherchez la section avec les informations de déploiement et supprimez tout ce qui est écrit dans «l'interface principale». C'est ce qui indique à Xcode le fichier Storyboard à charger au démarrage de l'application, mais comme nous venons de supprimer «Main.storyboard», Xcode ne trouvera pas ce fichier, ce qui entraînera le blocage de l'application.

Création d'un ViewController
Si vous exécutez l'application maintenant, un écran noir apparaîtra, puisque l'application n'a plus de source d'interface utilisateur, donc dans la partie suivante nous la créerons. Ouvrez "AppDelegate.swift" et à l'intérieur de l'
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)
, Collez ce fragment de code:
self.window = UIWindow(frame: UIScreen.main.bounds) let viewController = ViewController() self.window?.rootViewController = viewController self.window?.makeKeyAndVisible()
Ce code fournit une fenêtre d'interaction de l'utilisateur avec l'application, qui se trouve généralement dans "ViewController.swift". Pour vérifier rapidement que tout fonctionne, accédez à «ViewController.swift» et dans la méthode
viewDidLoad()
, insérez la ligne suivante:
self.view.backgroundColor = .blue
Exécutez maintenant l'application.
Pour naviguer entre les fichiers dans Xcode, utilisez les touches de raccourci «⇧⌘O», puis entrez le nom de fichier ou même le fragment de code que vous recherchez, et une liste de fichiers parmi lesquels vous pouvez choisir apparaîtra à l'écran.
Après avoir démarré l'application, cela devrait être le résultat sur l'écran de votre simulateur:

Bien sûr, nous n'utiliserons pas ce bleu dégoûtant, alors changez simplement l'arrière-plan en blanc en remplaçant
.blue
par
.white
dans
viewDidLoad ()
.
Développement de l'interface utilisateur
Pour créer notre interface utilisateur, nous utiliserons une
bibliothèque qui nous facilitera la vie. Pour installer PureLayout, vous devez d'abord ouvrir votre terminal et taper cd, puis un espace, faites glisser votre dossier de projet dans le terminal et appuyez sur "Entrée". Exécutez maintenant les commandes suivantes à l'intérieur du terminal:
- pod init
- installation de pod
Cela devrait être la sortie de votre terminal après la deuxième commande:

Après cela, fermez Xcode, ouvrez le dossier dans le Finder et vous devriez trouver "<votre nom de projet> .xcworkspace". C'est ce que nous ouvrirons pour accéder à notre application si jamais nous avons besoin d'utiliser CocoaPods. Maintenant, trouvez le fichier nommé «PodFile» et écrivez la ligne suivante sous la phrase
use_frameworks!
pod “PureLayout”
Exécutez à nouveau
pod install
dans votre terminal, puis générez votre projet en appuyant sur «Commande + B».
Pause café
Maintenant que tout est configuré, commençons par le vrai travail. Allez sur "ViewController.swift" et prenez une tasse de café, car voici à quoi ressemblera le résultat final:

Créer ImageView
Insérez la ligne d'
import PureLayout
sous
import UIKit
afin de pouvoir utiliser la bibliothèque de ce fichier. Ensuite, sous la déclaration de classe et en dehors de toute fonction, nous commençons par créer la variable
Avatar ImageView
paresseux (paresseux) comme suit:
lazy var avatar: UIImageView = { let imageView = UIImageView(image: UIImage(named: "avatar.jpg")) imageView.autoSetDimensions(to: CGSize(width: 128.0, height: 128.0)) imageView.layer.borderWidth = 3.0 imageView.layer.borderColor = UIColor.lightGray.cgColor imageView.layer.cornerRadius = 64.0 imageView.clipsToBounds = true return imageView }()
Quant à l'image, enregistrez sur le bureau toute image que vous utiliserez comme avatar et faites-la glisser dans Xcode dans le dossier <Your Project Name>, qui dans mon cas s'appelle «ContactCard», et cochez la case «Copier les éléments si nécessaire» .

Après cela, écrivez le nom de ce fichier avec son extension dans la déclaration UIImage au lieu de «avatar.jpg».
Pour ceux d'entre vous qui ne le savent pas, les variables paresseuses sont similaires aux variables ordinaires, sauf qu'elles ne sont pas initialisées (ou qu'un espace mémoire est alloué) jusqu'à ce qu'elles soient nécessaires ou appelées pour la première fois . Cela signifie que les variables paresseuses ne sont pas initialisées lorsque le contrôleur de vue est initialisé, mais attendent plutôt un point ultérieur quand elles sont vraiment nécessaires, ce qui économise de la puissance de traitement et de l'espace mémoire pour d'autres processus. Cela est particulièrement utile lors de l'initialisation des composants de l'interface utilisateur.
PureLayout en action
Comme vous pouvez le voir à l'intérieur de l'initialisation, la ligne
imageView.autoSetDimensions (to: CGSize (width: 128.0, height: 128.0))
est PureLayout en action. En une seule ligne, nous avons défini une limite pour la hauteur et la largeur de UIImageView, et toutes les NSLayoutConstraints nécessaires sont créées sans avoir besoin d'énormes appels de fonction. Si vous deviez créer des restrictions par programme, vous êtes probablement déjà tombé amoureux de cette merveilleuse bibliothèque.
Pour rendre cette image ronde, nous avons défini son rayon angulaire à la moitié de sa largeur ou de sa hauteur, soit 64,0 points. De plus, définissez la propriété
clipsToBounds
sur
true
, qui indique à l'image qu'elle doit écraser tout ce qui se trouve en dehors du rayon que nous venons de définir.
Ensuite, nous passons à la création d'une vue UIV qui servira de haut de la vue derrière l'avatar peint en gris. Déclarez la variable paresseuse suivante pour cette vue:
lazy var upperView: UIView = { let view = UIView() view.autoSetDimension(.height, toSize: 128) view.backgroundColor = .gray return view }()
Ajout de sous-vues
Avant de continuer, créons une
func addSubviews ()
qui ajoute les vues que nous venons de créer (et toutes les autres que nous allons créer) en tant que sous-vues au contrôleur de vue:
func addSubviews() { self.view.addSubview(avatar) self.view.addSubview(upperView) }
Ajoutez maintenant la ligne suivante à
viewDidLoad (): self.addSubviews ()
Définition de restrictions
Pour avoir une idée du chemin parcouru, fixons des limites à ces deux types. Créez une autre fonction appelée
func setupConstraints()
et insérez les contraintes suivantes:
func setupConstraints() { avatar.autoAlignAxis(toSuperviewAxis: .vertical) avatar.autoPinEdge(toSuperviewEdge: .top, withInset: 64.0) upperView.autoPinEdge(toSuperviewEdge: .left) upperView.autoPinEdge(toSuperviewEdge: .right) upperView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom) }
Maintenant à l'intérieur de
viewDidLoad()
appelez
setupConstraints()
, comme suit:
self.setupConstraints()
. Ajoutez ceci APRÈS avoir appelé
addSubviews()
. Cela devrait être la conclusion finale:

Mettez l'avatar au premier plan
Malheureusement, ce n'est pas ce que j'aimerais recevoir. Comme vous pouvez le voir, notre vue
upperView
se trouve au-dessus de l'avatar. Cela est dû au fait que nous avons ajouté un avatar en tant que sous-
subviews
avant la vue
upperView
, et puisque ces sous-vues sont situées sous une forme quelconque sur la pile, nous obtenons ce résultat. Pour résoudre ce problème, nous pouvons simplement remplacer ces deux lignes l'une avec l'autre, mais il y a une autre astuce que je veux vous montrer, à savoir:
self.view.bringSubview (toFront: avatar)
.
Cette méthode transfère l'avatar du bas de la pile vers le haut, alors choisissez la méthode que vous préférez. Bien sûr, pour plus de lisibilité, il est préférable d'ajouter des sous-vues dans l'ordre dans lequel elles doivent être affichées si elles se croisent, tout en se souvenant que les premières sous-vues ajoutées seront au bas de la pile, et donc toutes les autres vues qui se croisent apparaîtront au-dessus.
Et voici à quoi cela devrait ressembler:

Créer un contrôle segmenté
Ensuite, nous allons créer un contrôle segmenté, qui est une barre grise contenant trois sections. En fait, un contrôle segmenté est facile à créer. Procédez comme suit:
lazy var segmentedControl: UISegmentedControl = { let control = UISegmentedControl(items: ["Personal", "Social", "Resumè"]) control.autoSetDimension(.height, toSize: 32.0) control.selectedSegmentIndex = 0 control.layer.borderColor = UIColor.gray.cgColor control.tintColor = .gray return control }()
Je crois que tout est clair, la seule différence est qu'après l'initialisation, nous lui fournissons un tableau de chaînes, chaque ligne représente l'en-tête de l'une de nos sections souhaitées. Nous avons également défini
selectedSegmentIndex
sur 0, ce qui indique au contrôle segmenté de sélectionner / sélectionner le premier segment lors de l'initialisation. Le reste n'est qu'un style avec lequel jouer.
Continuons maintenant et ajoutons-le en tant que sous-vue en insérant la ligne suivante à la fin de la fonction
addCubviews(): self.view.addSubview(segmentedControl)
et ses limitations seront comme ceci:
segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: 8.0) segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0) segmentedControl.autoPinEdge(.top, to: .bottom, of: avatar, withOffset: 16.0)
Nous indiquons au contrôle segmenté que nous voulons l'attacher au côté gauche de sa vue d'ensemble, mais nous voulons augmenter légèrement l'intervalle, et non l'attacher directement au bord de l'écran. Si vous remarquez, j'utilise la soi-disant grille à huit points, où toutes les distances et tailles sont des multiples de huit. Je fais de même sur le côté droit du contrôle segmenté. Quant à la dernière limitation, il dit attacher un sommet à la base de l'avatar avec un intervalle de 16 points.
Après avoir ajouté les limitations ci-dessus à
func setupConstraints()
exécutez le code et assurez-vous qu'il ressemble à ceci:

Ajout d'un bouton
Passons maintenant à la dernière partie de l'interface utilisateur du manuel, qui est un bouton «Modifier». Ajoutez la variable paresseuse suivante:
lazy var editButton: UIButton = { let button = UIButton() button.setTitle("Edit", for: .normal) button.setTitleColor(.gray, for: .normal) button.layer.cornerRadius = 4.0 button.layer.borderColor = UIColor.gray.cgColor button.layer.borderWidth = 1.0 button.tintColor = .gray button.backgroundColor = .clear button.autoSetDimension(.width, toSize: 96.0) button.autoSetDimension(.height, toSize: 32.0) return button }()
Ne vous inquiétez pas de la taille de l'initialisation, mais faites attention à la façon dont je définis le titre et sa couleur en appelant les fonctions
button.setTitle
et
button.setTitleColor
. Pour certaines raisons, nous ne pouvons pas définir le titre d'un bouton en accédant directement à son
titleLabel
, et cela parce qu'il existe différents états pour le bouton, et il serait pratique pour beaucoup d'avoir des en-têtes / couleurs différents pour différents états.
Ajoutez maintenant le bouton en tant que sous-vue, comme le reste des composants, et ajoutez les restrictions suivantes pour qu'il apparaisse où il devrait être:
editButton.autoPinEdge(.top, to: .bottom, of: upperView, withOffset: 16.0) editButton.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0)
Ici, nous définissons uniquement les limites droite et supérieure du bouton, car nous lui avons donné une taille, il ne se développera pas et rien d'autre ne sera nécessaire. Exécutez maintenant le projet pour voir le résultat final:

Quelques notes récentes
Entrainez-vous, ajoutez autant d'éléments d'interface que vous le souhaitez. Créez des vues de toute application que vous trouvez difficile. Commencez simplement et augmentez progressivement la difficulté. Essayez de dessiner des composants d'interface utilisateur sur une feuille de papier pour imaginer comment ils s'emboîtent.
Dans la deuxième partie, j'étends ce guide pour créer une barre de navigation, une vue tabulaire et des cellules de taille dynamique dans le code.