BoxView - Autolayout pratique pour iOS

Je souhaite partager une bibliothèque pour créer efficacement l'interface utilisateur des applications iOS basées sur la mise en page automatique.



Bien qu'avec l'avènement de SwiftUI, la pertinence de la mise en page automatique diminue rapidement, alors que ce mécanisme est toujours activement utilisé, et la bibliothèque peut être utile pour ceux qui créent (ou modifient) l'interface utilisateur directement dans le code.



Cette méthode de construction d'une interface présente un certain nombre d'inconvénients qui limitent son utilisation:



  • La création d'éléments NSLayoutConstraint est très gênante.
  • Mauvaise visibilité - en regardant le code, il est difficile de comprendre à quoi ressemblera l'interface utilisateur.
  • Beaucoup de code de routine. Pour placer chaque vue, une moyenne d'environ 3 contraintes est requise, c'est-à-dire trois lignes du même code.
  • La complexité de la création d'interfaces à changement dynamique: vous devez enregistrer les contraintes dans des variables distinctes afin de pouvoir ensuite les modifier, et souvent créer des contraintes redondantes et désactiver les contraintes inutiles.


Le premier problème peut être facilement résolu en enveloppant les méthodes standard de création de contraintes dans quelque chose de plus humain. Et cela est déjà bien implémenté, par exemple, dans SnapKit , TinyConstraints et d'autres bibliothèques similaires.



Mais vous devez encore écrire beaucoup du même type de code, et il reste des problèmes de visibilité et de modifications dynamiques de la mise en page. UIStackView résout gracieusement ces problèmes, mais malheureusement, dans UIStackView, la disposition des éléments individuels est très limitée. Par conséquent, l'idée est née d'un conteneur UIView qui contrôle la disposition de la pile de ses sous-vues, mais avec la possibilité de personnaliser individuellement l'emplacement de chaque sous-vue.

C'est l'approche derrière BoxView et s'est avérée très efficace. BoxView vous permet d'éliminer presque complètement la création manuelle de contraintes, presque toute l'interface utilisateur est formée comme un système de BoxViews imbriquées. En conséquence, le code est devenu beaucoup plus court et plus visible, le gain est particulièrement perceptible pour les interfaces utilisateur dynamiques.



BoxView est à bien des égards similaire à UIStackView standard, mais il utilise des règles différentes pour le placement des sous-vues: vous pouvez y définir des retraits et des tailles pour chaque sous-vue individuellement. Pour créer une mise en page, BoxView utilise un tableau d'éléments de type BoxItem, qui contient toutes les vues à afficher et des informations sur la manière de les organiser. Et cela ne nécessite pas du tout beaucoup de code - la plupart des paramètres de mise en page sont pris par défaut, et seules les valeurs nécessaires sont explicitement spécifiées.



Une propriété essentielle de BoxView est qu'il crée uniquement les contraintes spécifiées pour les sous-vues ajoutées, et rien de plus. Par conséquent, il peut être utilisé sans aucune restriction avec toutes les autres bibliothèques et méthodes de mise en page.



À titre d'exemple, envisagez de créer une connexion par formulaire simple à l'aide de BoxView (l'exemple de code complet avec une description étape par étape est disponible dans le projet BoxViewExample sur github ).



image


Pour créer une telle mise en page sur BoxView, quelques lignes de code suffisent:



        nameBoxView.items = [nameImageView.boxed.centerY(), nameField.boxed]
        passwordBoxView.items = [passwordImageView.boxed.centerY(), passwordField.boxed]
        boxView.insets = .all(16.0)
        boxView.spacing = 20.0
        boxView.items = [
            titleLabel.boxed.centerX(padding: 30.0).bottom(20.0),
            nameBoxView.boxed,
            passwordBoxView.boxed,
            forgotButton.boxed.left(>=0.0),
            loginButton.boxed.top(30.0).left(50.0).right(50.0),
        ]


L'élément BoxItem est créé à partir de n'importe quel UIView à l'aide de la variable encadrée, après quoi il peut être défini pour indenter de 4 côtés, alignement, ainsi que des tailles absolues ou relatives.



Tous les éléments de mise en page peuvent être librement ajoutés et supprimés (y compris avec une animation) sans affecter le placement du reste. A titre d'exemple, ajoutons une vérification des champs de saisie vides et, en cas d'erreur, nous afficherons un message directement sous le champ vide:



image


Et bien que le message doive être "intégré" dans la mise en page existante, il ne nécessite même pas de changer le code existant!



    func showErrorForField(_ field: UITextField) {
        errorLabel.frame = field.convert(field.bounds, to: boxView)
        let item = errorLabel.boxed.top(-boxView.spacing).left(errorLabel.frame.minX - boxView.insets.left)
        boxView.insertItem(item, after: field.superview, z: .back)
        boxView.animateChangesWithDurations(0.3)
    }
    
    @objc func onClickButton(sender: UIButton) {
        for field in [nameField, passwordField] {
            if field.text?.isEmpty ?? true {
                showErrorForField(field)
                return
            }
        }
        // ok, can proceed with login
    }
    
    @objc func onChangeTextField(sender: UITextField) {
        errorLabel.removeFromSuperview()
        boxView.animateChangesWithDurations(0.3)
    }


BoxView prend en charge toutes les boîtes à outils de mise en page automatique: distances entre les éléments, tailles absolues et relatives, priorités, prise en charge du langage RTL. En plus de UIView, les objets invisibles - UILayoutGuides peuvent également être utilisés comme éléments de mise en page. La disposition Flex peut également être utilisée. Bien entendu, le schéma de disposition lui-même, sous la forme d'un système de piles UIView imbriquées, ne couvre pas à 100% toutes les options concevables pour la disposition relative des éléments, mais cela n'est pas obligatoire. C'est très bien pour la grande majorité des interfaces utilisateur typiques, et pour les cas plus exotiques, vous pouvez toujours ajouter les contraintes supplémentaires correspondantes de toute autre manière. Plusieurs méthodes utilitaires, par exemple, pour créer des contraintes de rapport hauteur / largeur, sont également incluses dans la bibliothèque.



Un autre petit exempledisponible sur github (~ 100 lignes de code!) illustre l'utilisation du système BoxView imbriqué en conjonction avec d'autres méthodes de définition de contraintes, ainsi que des modifications animées des paramètres de BoxView.



image


Projet BoxView sur github



All Articles