Comment créer une conception skeuomorphique avec SwiftUI

Online Coding Courses for Kids

Ne le niez pas. Vous ressentez le désir, cette absence pour rendre les cœurs plus affectueux. Vous aimez beaucoup éclater depuis la sortie d’iOS 7. Votre chère vous manque skeuomorphisme.

Lorsque vous regardez vos écrans aujourd’hui, ils ont l’air si plats et ennuyeux. Là encore, lorsque vous regardez ces conceptions antérieures à iOS 7, elles sont anciennes et obsolètes. Si seulement il y avait un langage de conception skeuomorphique qui avait l’air neuf et frais.

Vous êtes arrivé au bon endroit!

Fin 2019, un tweet a fait le tour des créations d’Alexandar Plyuto. Et ils sont fabuleux:

Conception skeuomorphique moderne

Dans ce didacticiel, vous apprendrez à recréer certains de ces éléments de conception dans SwiftUI. Vous découvrirez comment:

  • Utilisez des dégradés linéaires pour donner de la profondeur à vos vues.
  • Créez des effets complexes en combinant des effets simples tels que des ombres.
  • Maîtrisez l’art du masque inverse, un effet qui n’existe pas nativement dans SwiftUI.

Préparez-vous pour beaucoup de plaisir de conception!

Commencer

Les maisons intelligentes font fureur ces jours-ci. Pour ne pas être en reste, les super-méchants et les scientifiques fous sont désormais super dans les repaires intelligents. Hé, vous ne construisez pas votre QG dans une caldeira active sans câbler une technologie folle partout.

Voici le secret peu connu: l’application que les super-méchants utilisent pour contrôler leurs tanières – appelée SmartLair – est aussi plate et ennuyeuse que l’application Home sur votre iPhone. Peut-être plus encore.

Dans cet exercice, vous avez récupéré une copie du code source de SmartLair via des canaux de retour illicites. Vous avez été menacé, euh, embauché pour le raviver.

Pour commencer, cliquez sur le Télécharger les documents en haut ou en bas de ce didacticiel. Ouvrez le projet de début et explorez son contenu.

N’oubliez pas: c’est une grande opportunité pour vous. Si les méchants aiment votre travail, cela pourrait conduire à plus de contrats à l’avenir. Mais s’ils ne le font pas, eh bien, les requins laser ne sont pas connus pour être des mangeurs difficiles.

Présentation du gradient linéaire

Avant de commencer, vous devez vous familiariser avec LinearGradient. La conception skeuomorphe se penche fortement sur des gradients linéaires. C’est un gros problème.

Dans SwiftUI, vous définissez un dégradé linéaire comme ceci:

LinearGradient(
  gradient: Gradient(colors: [.white, .lairLightGray]),
  startPoint: UnitPoint(x: 0.2, y: 0.2),
  endPoint: .bottomTrailing
)

S’il est utilisé comme vue pour couvrir tout l’écran, il ressemble à ceci:

Exemple d'écran complet LinearGradient

Ici, vous définissez que le dégradé ira de white à lairLightGray, une Color vous ajouterez au projet plus tard. Vous pouvez avoir plus de deux couleurs si vous souhaitez que le dégradé passe par plusieurs:

LinearGradient utilisant plusieurs couleurs

startPoint et endPoint sont des coordonnées par rapport au carré de l’unité, qui a une coordonnée de (0, 0) en haut à gauche et (1, 1) en bas à droite. Cependant, ils ne sont pas tenus d’être dans cette plage. Par exemple, une valeur de coordonnées négatives démarrerait le dégradé en dehors de la vue.

Dans le code ci-dessus, il existe également des constantes prédéfinies pour les points de début et de fin typiques, tels que .leading, .trailing, .top, .bottom et leurs combinaisons.

Personnalisation de votre premier élément

Commencez par attaquer le regard ennuyeux AccessoryView. Il représente les grands rectangles au milieu de l’écran avec des étiquettes telles que Lumières de la salle de contrôle et Donjon.

AccessoryView starter

Faites glisser le Extensions dossier des matériaux téléchargés vers votre projet Xcode. Placez-le au-dessus du Vues groupe. Assure-toi Copiez les éléments si nécessaire et Créer des groupes sont sélectionnés, puis cliquez sur terminer.

Ces trois fichiers définissent certains UIColor constantes, le SwiftUI Color équivalents, et certains LinearGradient définitions. Vous avez déjà vu comment créer un LinearGradient, mais la conception skeuomorphe utilise un lot des gradients. Il faut trop de temps pour les parcourir un par un, et les super-méchants ne sont pas du type patient, vous y arriverez donc directement.

Inclusion du dégradé d’image

Dans AccessoryView.swift selon la définition de body, trouvez la ligne qui commence par image dans le VStack. Remplacez cette ligne par le code ci-dessous, mais ne supprimez pas les modificateurs frame, padding et font qui sont là:

LinearGradient.lairHorizontalDark
  .mask(image.resizable().scaledToFit())

Vous venez de transformer l’image SFSymbol en masque pour le dégradé. Le calque avec le dégradé sera découpé sous la forme des pixels opaques de l’image. Cool! Vous pouvez créer et exécuter pour voir les modifications ou activer le canevas d’aperçu dans Xcode pour voir les modifications immédiatement:

Symbole de dégradé AccessoryView

Ajout de surbrillance et d’ombre

Ajoutez ce code après font:

// 1
.shadow(color: .white, radius: 2, x: -3, y: -3)

// 2
.shadow(color: .lairShadowGray, radius: 2, x: 3, y: 3)

Avec ces deux lignes, vous avez:

  1. Ajout d’une ombre blanche décalée par rapport au coin supérieur gauche de l’image.
  2. Ajout d’une ombre sombre décalée par rapport au coin inférieur droit.

Ce contraste donne l’illusion de la profondeur dans toutes les directions. Vous pouvez utiliser cette astuce d’ombre pour obtenir un effet surélevé.

AccessoireVoir le symbole final

Le code de l’élément avec tous les modificateurs devrait maintenant ressembler à ceci:

LinearGradient.lairHorizontalDark
  .mask(image.resizable().scaledToFit())
  .frame(width: 150, height: 236)
  .padding(40)
  .font(.system(size: 150, weight: .thin))
  .shadow(color: .white, radius: 2, x: -3, y: -3)
  .shadow(color: .lairShadowGray, radius: 2, x: 3, y: 3)

Modification du dégradé de texte

Le texte ne peut clairement pas rester noir. Ajoutez le modificateur suivant à Text dans le HStack:

.foregroundColor(.lairDarkGray)

Maintenant, votre texte est une jolie teinte de repaire gris foncé.

Couleur du texte

Le complet HStack devrait ressembler à ceci maintenant:

HStack {
  Text(title)
    .foregroundColor(.lairDarkGray)
    .bold()
    .padding(.leading)
    .padding(.bottom)
  Spacer()
}

Notez que si vous mettez foregroundColor dans un endroit différent parmi Text, ça fonctionne encore. Vous pouvez commander certains modificateurs de n’importe quelle façon, mais comme vous le verrez, l’ordre est important pour les autres.

Arrondir les coins

Votre prochaine étape consiste à arrondir les coins de bordure. Ce n’est pas aussi simple qu’il y paraît, cependant.

Par exemple, essayez quelque chose comme ceci:

.border(Color.gray, width: 1)
.cornerRadius(15)

Et vous voyez que les coins sont coupés.

Couper les coins des bordures

Échangez les deux modificateurs comme suit:

.cornerRadius(15)
.border(Color.gray, width: 1)

Et vous voyez que les bordures maintiennent des angles vifs.

Coins de bordure d'angle vif

Heureusement, il existe une solution de contournement. Vous pouvez utiliser une superposition pour obtenir ces bordures courbes douces et douces.

Supprimer border et, si vous l’ajoutez, cornerRadius. Remplacez-les par:

.overlay(
  RoundedRectangle(cornerRadius: 15)
    .stroke(LinearGradient.lairDiagonalDarkBorder, lineWidth: 2)
)

Ce code établit une autre vue sur votre vue – c’est-à-dire une superposition – qui dessine un rectangle arrondi avec le rayon de coin souhaité.

Pour les structures conformes à Shape, tel que RoundedRectangle ou Path, utilisation stroke au lieu de border pour tracer une ligne autour d’elle.

Avec cette modification, vous ajoutez également un dégradé à la bordure en la caressant avec LinearGradient.lairDiagonalDarkBorder au lieu de Color.gray. Cet ajout donne à la bordure un reflet brillant en haut à gauche de l’élément et une ombre plus foncée en bas à droite. Simultanément, vous alourdissez la bordure en augmentant la largeur de la bordure / du trait à «2».

AccessoireVoir bordure arrondie

Vous remarquerez peut-être que les bordures supérieure et inférieure sont plus minces que la gauche et la droite. En effet, la vue n’a pas de remplissage vertical et coupe la moitié du trait. Pas de soucis. Vous réglerez cela dans un instant.

Faire ressortir cette frontière

À l’heure actuelle, vous voulez que les sections blanches de la bordure et les reflets se détachent. Il est temps de changer la couleur d’arrière-plan de l’élément.

Après la parenthèse fermante pour overlay, ajoutez les lignes suivantes:

.background(Color.lairBackgroundGray)
.cornerRadius(15)

Puisqu’un arrière-plan peut être View et pas seulement une couleur, il faut la passer Color.lairBackgroundGray. C’est différent de foregroundColor, qui ne peut Color.

Couleur de l'arrière plan

Modification de la forme de la bordure

Vous vous demandez peut-être: «Qu’est-ce qui se passe avec le cornerRadius? N’était-ce pas déjà pris en charge avec la bordure que vous avez créée en utilisant le overlay?

Sorte de. cornerRadius défini que la bordure aurait un rayon d’angle. Cependant, la bordure est une superposition en haut de la vue. Ça signifie cornerRadius ne modifie pas la forme de la vue. Vous devrez toujours modifier vous-même la forme sous-jacente.

Si vous commentez cornerRadius, vous verrez que votre vue comporte toujours des angles vifs et que votre bordure n’est qu’une superposition.

Coins d'arrière-plan visibles

C’est moche. Mais décommentez à nouveau le modificateur et tout revient à la normale.

AccessoryView semble beaucoup mieux. Mais il n’apparaît plus maintenant et le symbole au milieu manque de profondeur. Pour ajouter cette profondeur à la vue, utilisez la même technique que vous avez utilisée avec le symbole: reflets et ombres.

Juste en dessous cornerRadius, ajoutez ce qui suit:

.shadow(
  color: Color(white: 1.0).opacity(0.9),
  radius: 18, 
  x: -18, 
  y: -18)
.shadow(
  color: Color.lairShadowGray.opacity(0.5),
  radius: 14, 
  x: 14, 
  y: 14)

Puisque la première ombre est blanche, elle agit comme un reflet. La deuxième ombre est votre, eh bien, l’ombre.

Dépannage d’AccessoryView

Si vous exécutez l’application, vous rencontrerez deux problèmes qui vous empêcheront de voir AccessoryView dans toute sa splendeur.

Problèmes avec AccessoryView

Ici vous voyez que:

  1. L’arrière-plan reste blanc, donc le reflet ne peut pas être vu contre lui.
  2. AccessoryViewRow n’a pas de remplissage vertical, donc l’ombre et le reflet sont coupés.

Pour résoudre le premier problème, ouvrez LairView.swift et intégrer NavigationView«S VStack dans un ZStack. Commande + clic sur VStack et sélectionnez Intégrer dans HStack (il n’y a pas d’option pour intégrer dans un ZStack. Puis changez HStack à un ZStack.

LairView intégré dans ZStack

Ajoutez ce qui suit comme premier élément dans le nouveau ZStack juste au-dessus du VStack:

Color.lairBackgroundGray.edgesIgnoringSafeArea(.all)

Cela ajoute Color dans la couleur d’arrière-plan souhaitée, ce qui lui permet de remplir l’écran en ignorant tous les bords de la zone de sécurité.

Pour résoudre le deuxième problème, ouvrez AccessoryRowView.swift. Ajoutez ensuite les deux modificateurs suivants à l’ensemble HStack dans ScrollView:

.padding(.top, 32)
.padding(.bottom, 38)

Ce code ajoute un remplissage en haut et en bas de la vue. Très cool.

Vous avez maintenant terminé avec AccessoryView. Construisez et exécutez.

AccessoryView terminé

Ça commence à bien paraître!

Présentation des modificateurs intégrés

Vous avez utilisé quelques View des modificateurs pour personnaliser votre premier élément. Mais SwiftUI arbore un tonne de modificateurs intégrés. Par exemple:

  • animation: Ceci applique une animation à la vue.
  • clipShape: Cela définit une forme de détourage pour la vue.
  • onAppear: Cela permet à du code de s’exécuter lorsque la vue apparaît.
  • rotationEffect: Cela fait pivoter la vue autour d’un point donné.

Si la liste complète vous intéresse, consultez Documentation d’Apple.

Il existe un modificateur pour presque tout ce que vous pourriez souhaiter faire. Presque.

Découvrir les masques inverses

Avant de pouvoir attaquer la barre d’onglets, vous devez vous renseigner sur les masques inverses.

Depuis Apple inclus mask, vous penseriez que cela inclurait également inverseMask donc tout ce qui est opaque pourrait couper un «trou» dans la couche ci-dessous. Eh bien, Apple ne l’a pas fait.

Vous devrez créer votre propre modificateur pour cela. Ajouter un nouveau Fichier Swift à la Extensions groupe. Nomme le ViewExtension.swift.

Remplacez ensuite le contenu par le code suivant:

import SwiftUI

extension View {
  // 1
  func inverseMask(_ mask: Mask) -> some View where Mask: View {
    // 2
    self.mask(mask
      // 3
      .foregroundColor(.black)
      // 4
      .background(Color.white)
      // 5
      .compositingGroup()
      // 6
      .luminanceToAlpha()
    )
  }
}

Avec cette poignée de lignes, vous avez:

  1. Défini un nouveau inverseMask qui imite mask.
  2. Retourne la vue actuelle masquée avec le masque de saisie et modifiée.
  3. Définissez la couleur de premier plan du masque de saisie sur noir.
  4. Assurez-vous que l’arrière-plan du masque de saisie est blanc uni.
  5. Enveloppé le masque de saisie dans un groupe de composition.
  6. Converti la luminance en alpha, rendant le premier plan noir transparent et gardant le fond clair opaque – c’est-à-dire un masque inverse!

Il convient de souligner que cela ne fonctionnerait pas sans compositingGroup. Avant de créer le groupe de composition, l’arrière-plan était un calque blanc uni et le premier plan était une image noire avec un arrière-plan transparent posé sur le haut de l’arrière-plan blanc.

Pas de groupe de compositing

Si vous appelez luminaceToAlpha, le premier plan noir devient transparent et l’ensemble du fond blanc uni devient visible.

Pas de composition de groupe luminaceToAlpha

Mais en utilisant compositingGroup, vous disposez d’un calque à rendu unique composé de pixels noirs et blancs.

Groupe de compositing

Après avoir couru luminanceToAlpha, vous obtenez le premier plan sombre coupé de la vue.

Compositing group luminaceToAlpha

Phew! Il est temps d’utiliser ce nouvel effet sur les boutons de la barre d’onglets!

Tackling Tab Bar Buttons

Cette étape est la plus impliquée dans le didacticiel. Une partie du problème est l’utilisation de masques inverses comme requis par le concepteur. Mais, bien sûr, vous avez déjà résolu cette partie.

L’autre partie concerne les options limitées dont vous disposez pour personnaliser les boutons de la barre d’onglets. Heureusement, les développeurs précédents de SmartLair ne savait pas comment utiliser correctement TabView, ils l’ont donc implémenté manuellement. Cela rend votre travail plus facile! Quant aux développeurs précédents, qu’ils reposent en paix.

Même ainsi, vous devez toujours concevoir un aspect sélectionné et non sélectionné pour les boutons.

Pour commencer, ouvrez TabBarItemView.swift et ajoutez la constante suivante au-dessus de la définition de body:

let size: CGFloat = 32

Oui, c’est une taille codée en dur. Ne vous inquiétez pas. C’est juste pour le tutoriel. Vous pouvez le corriger dans une version ultérieure. ;]

Juste en dessous de la constante, ajoutez une fonction d’assistance:

func isSelected() -> Bool {
  return selectedItem == smartView
}

Cette fonction fait exactement ce qu’elle dit sur l’étain: elle demande si l’élément actuel de la barre d’onglets est sélectionné. Il le fait en vérifiant si la borne selectedItem correspond à sa définition SmartView.

Comme il s’agit d’un bouton bascule, la fonction peut vous aider à déterminer comment présenter le bouton de la barre d’onglets.

Ensuite, ajoutez les talons suivants au bas de TabBarItemView:

var buttonUp: some View {
  EmptyView()
}

var buttonDown: some View {
  EmptyView()
}

Vous les utiliserez pour mieux organiser l’apparence des éléments de la barre d’onglets lorsqu’ils sont de haut en bas. Pour l’instant, le EmptyViews sont des espaces réservés pour empêcher Xcode de vous harceler.

Maintenant, mettez à jour le Button dans le body ressembler à ceci:

Button(action: {
  self.selectedItem = self.smartView
}) {
  // This is the new stuff!
  if isSelected() {
    buttonDown
  } else {
    buttonUp
  }
}

Vous avez remplacé le Image avec une condition pour décider de l’état du bouton à présenter à l’utilisateur.

Il ne reste plus qu’à concevoir l’apparence des boutons.

Conception du bouton de barre d’onglets non sélectionné

Vous commencerez avec le bouton non sélectionné et le concevrez de la même manière que AccessoryView. Il y a cependant une différence. Au lieu de faire en sorte que le symbole soit élevé au-dessus de la surface, vous le ferez couper de la surface. C’est l’heure du masque inverse!

Remplacer buttonUp avec:

var buttonUp: some View {
  // 1
  var buttonMask: some View {
    // 2
    ZStack {
      // 3
      Rectangle()
        .foregroundColor(.white)
        .frame(width: size * 2, height: size * 2)
      // 4
      Image(systemName: self.icon)
        .resizable()
        .scaledToFit()
        .frame(width: size, height: size)
    }
  }
    
  // 5
  return buttonMask
}

Dans ce code, vous avez:

  1. Défini une propriété dans une propriété pour stocker le masque que vous utiliserez pour le bouton. Cela gardera le code un peu plus lisible.
  2. Utilisé un ZStack comme vue de niveau supérieur pour le masque.
  3. Défini un blanc Rectangle pour servir d’arrière-plan du masque.
  4. Créé un Image c’est-à-dire la moitié de la largeur et de la hauteur du rectangle d’arrière-plan. Cette image sera ce qui sera coupé lorsque vous transformerez ce bouton en masque inverse.
  5. Retourné le masque, vous pouvez donc voir à quoi il ressemble. Vous remplacerez cette ligne après avoir vérifié qu’elle est correcte.

Vous devriez voir une icône très simple au milieu de l’aperçu du canevas.

Aperçu du masque des boutons

Remarque: Apple peut appeler cette icône crayon.tip, mais dites simplement à vos clients Volcano Lair.

Ensuite, remplacez return buttonMask avec ce qui suit:

// 1
var button: some View {
  // 2
  ZStack {
    // 3
    Rectangle()
      .inverseMask(buttonMask)
      .frame(width: size * 2, height: size * 2)
      .foregroundColor(.lairBackgroundGray)
    }
  }

// 4
return button

Vous avez ici:

  1. Défini une autre propriété pour le bouton réel.
  2. Utilisé un ZStack pour contenir tous les éléments. Il y en aura d’autres à venir.
  3. A créé un Rectangle qui utilise buttonMask comme masque inverse!
  4. Retourna le bouton.

Si vous regardez l’aperçu de la toile, vous voyez enfin les fruits de votre travail de masque inverse!

Masque inverse de bouton

Ajout d’effets de bouton de barre d’onglets non sélectionnés

Un bouton avec un trou n’est pas spectaculaire en soi, vous y ajouterez donc encore plus d’effets!

Juste au-dessus du button«S Rectangle, mais toujours à l’intérieur ZStack, ajoutez ce qui suit LinearGradient:

LinearGradient.lairHorizontalDarkReverse
  .frame(width: size, height: size)

Cette LinearGradient est juste assez grand pour couvrir la découpe du symbole dans le bouton et être visible à travers la découpe.

LinearGradient visible à travers la découpe

Ensuite, ajoutez les modificateurs suivants à la button«S Rectangle juste après foregroundColor:

.shadow(color: .lairShadowGray, radius: 3, x: 3, y: 3)
.shadow(color: .white, radius: 3, x: -3, y: -3)
.clipShape(RoundedRectangle(cornerRadius: size * 8 / 16))

Ici, vous avez ajouté des reflets et des ombres mais dans la direction opposée d’avant. C’est parce que vous voulez qu’ils affectent la découpe au milieu du bouton. le clipShape non seulement arrondit les coins du bouton; il contient également les reflets et les ombres dans ces limites. S’ils fuyaient, cela n’aurait pas l’air bien.

Forme du clip et ombres intérieures

Enfin, ajoutez ces effets à l’ensemble ZStack:

.compositingGroup()
.shadow(
  color: Color.white.opacity(0.9),
  radius: 10, 
  x: -5, 
  y: -5)
.shadow(
  color: Color.lairShadowGray.opacity(0.5),
  radius: 10, 
  x: 5, 
  y: 5)

Tout d’abord, assurez-vous que toutes les vues ZStack sont dans un groupe de compositing. Ajoutez ensuite le reflet et l’ombre typiques pour donner au bouton un aspect en relief. Votre bouton non sélectionné ressemble maintenant à ceci:

Bouton non sélectionné avec fond blanc

Et lorsque vous obtenez la bonne couleur d’arrière-plan, cela ressemblera à ceci:

Bouton non sélectionné avec un arrière-plan correct

Conception du bouton Sélectionner la barre d’onglets

Un état non sélectionné pour un bouton ne suffit pas. Vous devrez également ajouter l’état sélectionné.

Avant de commencer, faites défiler vers le bas de TabBarItemView.swift à TabBarItemView_Previews. Dans la liste des paramètres de l’aperçu TabBarItemView, changer la selectedItem être .constant(SmartView.lair):

struct TabBarItemView_Previews: PreviewProvider {
  static var previews: some View {
    TabBarItemView(
      selectedItem: .constant(SmartView.lair),
      smartView: .lair, 
      icon: "pencil.tip")
  }
}

Cela présente le bouton tel qu’il est sélectionné dans le canevas d’aperçu, de sorte que vous pouvez voir les modifications lorsque vous les apportez.

D’ACCORD. Maintenant, remplacez l’implémentation actuelle de buttonDown avec ce qui suit:

var buttonDown: some View {
  ZStack {
    Rectangle()
      .foregroundColor(.lairBackgroundGray)
      .frame(width: size * 2.25, height: size * 2.25)
      .cornerRadius(size * 8 / 16)
  }
}

Ici, vous avez défini la forme, la couleur et la taille du bouton lorsqu’il est sélectionné.

Arrière-plan du bouton sélectionné

Malheureusement, il est un peu plus grand que le bouton non sélectionné. Voici l’effet de coupe que vous photographiez sous un angle différent:

Section de bouton

En tant que tel, vous devez le faire légèrement plus grand pour tenir compte du bord extérieur du bouton sélectionné. Auparavant, cela aurait été «masqué» dans les hautes lumières et les ombres, mais maintenant il doit être visible.

Ajoutez ce qui suit Rectangle en dessous de celui que vous venez de créer:

Rectangle()
  .foregroundColor(.lairBackgroundGray)
  .frame(width: size * 2.25, height: size * 2.25)
  .cornerRadius(size * 8 / 16)
  .inverseMask(Rectangle()
    .cornerRadius(size * 6 / 16)
    .padding(size / 8)
  )

L’aperçu est exactement le même, mais n’ajustez pas votre écran. Au lieu de cela, changez foregroundColor de cela Rectangle à .blue. Voyez ce qui se passe.

Bouton bleu sélectionné bordure

Vous avez créé une bordure autour du bouton qui est invisible, mais vous n’avez pas utilisé la même astuce de superposition que précédemment. C’est parce qu’un masque inverse vous permettra de créer une ombre à l’intérieur du bouton, contrairement à la superposition.

Changer la .blue retour à .lairBackgroundGray.

Maintenant, ajoutez ces modificateurs à la Rectangle après inverseMaskParenthèse fermante:

.shadow(
  color: Color.lairShadowGray.opacity(0.7),
  radius: size * 0.1875,
  x: size * 0.1875, 
  y: size * 0.1875)
.shadow(
  color: Color(white: 1.0).opacity(0.9),
  radius: size * 0.1875,
  x: -size * 0.1875, 
  y: -size * 0.1875)
.clipShape(RoundedRectangle(cornerRadius: size * 8 / 16))

Ceux-ci ajoutent l’ombre intérieure typique et la surbrillance au masque inverse et coupent la forme extérieure du bouton de sorte que les ombres ne saignent pas de l’autre côté.

Bouton sélectionné sans logo

Cela commence à ressembler à un bouton sur lequel vous avez appuyé!

Intégration du symbole du bouton

Vous allez maintenant ajouter le symbole du bouton. Vous allez le rendre légèrement plus lourd, c’est-à-dire plus sombre, pour montrer que le bouton a été sélectionné. Vous allez également ignorer le masque inverse cette fois.

Ajoutez ce qui suit sous le dernier Rectangle et tous ses modificateurs:

LinearGradient.lairHorizontalDarkReverse
  .frame(width: size, height: size)
  .mask(Image(systemName: self.icon)
    .resizable()
    .scaledToFit()
  )
  .shadow(
    color: Color.lairShadowGray.opacity(0.5),
    radius: size * 0.1875,
    x: size * 0.1875, 
    y: size * 0.1875)
  .shadow(
    color: Color(white: 1.0).opacity(0.9),
    radius: size * 0.1875,
    x: -size * 0.1875, 
    y: -size * 0.1875)

Avec cette vue, vous utilisez l’icône du bouton pour masquer LinearGradient à la taille appropriée, puis ajoutez des reflets et des ombres.

Bouton sélectionné avec logo

Il y a un dernier effet à ajouter: une jolie bordure dégradée autour du bouton. Après l’accolade de fermeture du ZStack, ajoutez la superposition suivante:

.overlay(
  RoundedRectangle(cornerRadius: size * 8 / 16)
    .stroke(LinearGradient.lairDiagonalLightBorder, lineWidth: 2)
  )

Cette superposition définit une bordure qui est un rectangle arrondi d’une largeur de deux points et utilise un dégradé linéaire diagonal.

Bouton final sélectionné avec fond blanc

Encore une fois, voici à quoi cela ressemblera avec la bonne couleur d’arrière-plan:

Bouton final sélectionné avec un arrière-plan correct

Ranger les détails

Avant de créer et d’exécuter, vous apporterez quelques petites modifications au code dans ContentView.swift.

Tout d’abord, recherchez et supprimez les éléments suivants Rectangle:

Rectangle()
  .frame(height: 1.0 / UIScreen.main.scale)
  .foregroundColor(Color(white: 0.698))

Cela a défini une ligne d’un pixel entre la barre d’onglets et le reste de l’écran, mais ce n’est plus nécessaire.

Maintenant, changez le padding et le backgroundColor du TabBarView pour ça:

.padding(.bottom, geometry.safeAreaInsets.bottom / 2)
.background(Color.lairBackgroundGray)

Ici, vous avez réduit de moitié la quantité de rembourrage pour abaisser les boutons de la barre d’onglets et donner le reste de la salle de contenu. Vous avez également assorti TabBarViewLa couleur de fond de celle du NavigationView.

Construisez et courez, et voyez jusqu’où vous en êtes.

Barre d'onglets finale

Pas trop mal!

Fabrication de la barre de progression

Vous êtes dans la dernière ligne droite. Il y a un dernier élément d’interface utilisateur à résoudre.

Pour obtenir ce look crapuleux, la barre de progression s’appuiera moins sur les ombres et plus sur les dégradés linéaires. La nature longue et mince de la barre vous permet d’effectuer cette substitution.

Ouvert ProgressBarView.swift et jetez un œil à ce qui existe actuellement.

Il est divisé en deux sections principales: HStack qui contient toutes les étiquettes et un ZStack pour dessiner la barre de progression. Si vous regardez le ZStack, vous voyez qu’il est composé de deux Capsules. L’un est pour la longueur totale de la barre, l’autre pour la progression. Capsule est une forme incluse dans SwiftUI. But!

Tout d’abord, mettez à jour les deux Text pour que votre HStack ressemble à ça:

HStack {
  Text(self.title)
    .foregroundColor(.lairDarkGray)
    .bold()
  Spacer()
  Text("(Int(self.percent * 100))%")
    .foregroundColor(.lairDarkGray)
    .bold()
}

Le texte en gras donne à la barre un aspect plus prononcé, tandis que la couleur gris foncé supprime un contraste sévère que la couleur noire par défaut avait.

Mise à jour du texte de la barre de progression

Ensuite, dans la barre de progression ZStack section, intégrer le premier Capsule en autre ZStack, changez la hauteur du cadre en 14 et la couleur de premier plan .lairBackgroundGray.

La première capsule devrait maintenant ressembler à ceci:

ZStack {
  Capsule()
    .frame(height: 14)
    .foregroundColor(.lairBackgroundGray)
}

Vous avez utilisé une couleur légèrement plus claire pour la capsule pour correspondre à votre palette de couleurs actuelle. L’augmentation de la hauteur donnera bientôt l’impression que la barre de progression est assise dans une rainure taillée pour elle.

Sous ce même Capsule, mais toujours dans le nouveau ZStack, Ajoute ça LinearGradient:

LinearGradient.lairHorizontalDarkToLight
  .frame(height: 14)
  .mask(Capsule())
  .opacity(0.7)

le LinearGradient.lairHorizontalDarkToLight commence sombre en haut, passe à 100% de couleur claire au milieu et se termine par du blanc en bas. Vous l’utilisez pour simuler des effets d’ombre et de surbrillance. La couleur claire au milieu du dégradé permet la couleur du Capsule pour briller à travers. On dirait maintenant que c’est une rainure découpée dans l’iPhone.

Vous définissez également la hauteur du cadre pour correspondre à la précédente Capsule et utilisé un Capsule comme un masque pour s’assurer qu’il a la même forme. Enfin, l’opacité laisse plus de lairBackgroundGray couleur à travers.

Arrière-plan de la barre de progression

Modification de la deuxième capsule

Ensuite, consultez le Capsule qui dessine les progrès réels. C’est actuellement juste une goutte bleue ennuyeuse. Remplacer Capsule et ses deux modificateurs avec les éléments suivants:

// 1
ZStack {
  // 2
  LinearGradient.lairHorizontalLight
    // 3
    .frame(
      width: (geometry.size.width - 32) * CGFloat(self.percent),
      height: 10)
    // 4
    .mask(
      Capsule()
        .padding(.horizontal, 2)
    )
}
  1. A créé un ZStack pour contenir le LinearGradient et le deuxième que vous ajouterez ensuite.
  2. Ajout d’un dégradé horizontal, qui montrera l’importance de la longueur de la barre de progression, allant du clair au foncé.
  3. Utilisé la même taille de cadre que le Capsule tu as supprimé. Il utilise le percent et la taille de la vue pour calculer sa largeur.
  4. Masqué le dégradé avec un Capsule puisque, par défaut, un LinearGradient est un rectangle. Vous avez également rembourré le Capsule pour lui donner un décalage agréable par rapport à la rainure dans laquelle il se trouve.

Barre de progression au premier plan avec dégradé horizontal

Ajouter le suivant LinearGradient juste en dessous de la précédente, mais toujours dans le ZStack:

LinearGradient.lairVerticalLightToDark
  .frame(
    width: (geometry.size.width - 32) * CGFloat(self.percent),
    height: 10)
  .mask(
    Capsule()
      .padding(.horizontal, 2)
  )
  .opacity(0.7)

Ce gradient est très similaire. Les seules différences sont que le dégradé est vertical et l’opacité est de 0,7. Ce dégradé simule les ombres et les reflets mais dans la direction opposée au dégradé de groove. Cela donne l’impression que la barre de progression repose à l’intérieur de la rainure.

Barre de progression au premier plan avec les deux dégradés

Approfondir la barre de progression

Pour donner à la barre de progression une certaine profondeur, vous souhaitez inclure une ombre à la forme entière dans la rainure. Ajoutez l’ombre suivante à la ZStack contenant les deux LinearGradients:

.shadow(
  color: Color.lairShadowGray.opacity(0.5),
  radius: 2, 
  x: 0, 
  y: 1)

Parce que cette ombre saigne également à l’extérieur de la rainure, vous devez couper le niveau supérieur ZStack (celui avec l’alignement principal, pour vous aider à faire correspondre les accolades):

.clipShape(Capsule())

Voici à quoi devrait ressembler la barre de progression à l’écran:

Barre de progression finale

C’était une section compliquée, donc si vous ne voyez pas les résultats que vous attendez, comparez avec le projet final inclus dans les matériaux.

Navigation dans la barre de navigation

Deux parties de la vue de navigation ne correspondent pas encore à votre nouvelle application: le titre de la barre de navigation et l’icône de profil.

Pour le titre de la barre de navigation, vous devez utiliser un proxy d’apparence. Ouvert LairView.swiftet ajoutez ceci init à LairView:

init() {
  UINavigationBar.appearance().largeTitleTextAttributes =
    [.foregroundColor: UIColor.lairDarkGray]
}

Vient ensuite le profileView. Voilà votre défi! Modifiez-le pour utiliser un LinearGradient.lairHorizontalDark. Après avoir terminé ce tutoriel, vous savez tout ce dont vous avez besoin pour le faire vous-même!

Astuce: vous devrez coder en dur la taille à 22 × 22 points.

[spoiler title=”Solution”]

var profileView: some View {
  LinearGradient.lairHorizontalDark
    .frame(width: 22, height: 22)
    .mask(
      Image(systemName: "person.crop.circle")
        .resizable()
        .scaledToFit()
    )
  .padding()
}

[/spoiler]

Lorsque vous avez terminé, l’application terminée devrait être phénoménale!

Application finale

Où aller en partant d’ici?

Woohoo! Vous avez atteint la fin de ce didacticiel. Vous avez clairement un bel avenir devant vous en tant qu’ingénieur iOS principal pour tous les méchants méchants!

Vous pouvez télécharger la version complète du projet en utilisant le Télécharger les documents en haut ou en bas de ce didacticiel.

Bien que vous ayez conçu quelques éléments dans ce style skeuomorphique moderne, il y en a d’autres à essayer. Par exemple, vous pouvez attaquer un commutateur, un curseur ou une barre de recherche. Vous pouvez également faire passer cette conception skeuomorphique au niveau supérieur avec des haptiques. Ce serait vraiment quelque chose de spécial!

Si vous souhaitez approfondir SwiftUI, consultez Comment créer un écran de démarrage avec SwiftUI, Prise en main des animations SwiftUI ou le SwiftUI par Tutoriels livre disponible sur ce site.

Merci d’avoir essayé ce tutoriel. Si vous avez des questions ou des commentaires, rejoignez la discussion du forum ci-dessous!

Close Menu