SwiftUI: Connaissance

Dans ce guide, nous allons apprendre à planifier des applications d'interface utilisateur à l'aide de View et à utiliser des variables d'état pour modifier l'interface utilisateur.

Temps de lecture estimé: 25 minutes.

SwiftUI nous permet d'oublier complètement Interface Builder (IB) et les storyboards. IB et Xcode étaient des applications distinctes avant Xcode 4, mais la «connexion» entre elles est toujours visible lorsque nous modifions le nom IBAction ou IBOutlet et que notre application plante, donc IB ne sait rien des changements dans le code. Ou lorsque nous définissons des identificateurs pour les séquences ou pour les cellules du tableau, mais Xcode ne peut pas les vérifier, car ce sont des chaînes.

SwiftUI à la rescousse! Nous voyons immédiatement les changements dans la vue dès que nous entrons le code. Les modifications d'un côté entraînent une mise à jour de l'autre, elles sont donc toujours liées. Il n'y a aucun identifiant de chaîne qui pourrait être confondu. Et c'est tout le code, mais il est beaucoup plus petit que si nous l'avons écrit en utilisant UIKit, il est donc plus facile à comprendre, à modifier et à déboguer. Dites-moi, n'est-ce pas merveilleux?

On y va


Commençons un nouveau projet dans le projet Xcode ( Shift-Command-N ), sélectionnez iOS App Single View App , appelons RGBullsEye et assurez-vous de choisir SwiftUI comme interface.

Maintenant, AppDelegate.swift est divisé en deux fichiers: AppDelegate.swift et SceneDelegate.swift , et SceneDelegate contient une fenêtre:



SceneDelegate n'est presque pas lié à SwiftUI, à l'exception de cette ligne:

window.rootViewController = UIHostingController(rootView: ContentView()) 

UIHostingController crée un contrôleur de vue pour SwiftUI-view ContentView .
Remarque: UIHostingController nous permet d'intégrer SwiftUI-view dans une application existante. Ajoutez le Hosting View Controller à notre storyboard et créez un enchaînement dessus depuis UIViewController. Ensuite, nous utilisons Control-drag avec segue sur le code du contrôleur de vue pour créer une IBSegueAction , où nous définissons le contrôleur d'hébergement sur rootView - SwiftUI-view.
Lorsque l'application démarre, la fenêtre affiche une instance de ContentView , qui est définie dans le fichier ContentView.swift . Il s'agit d'une structure conforme au protocole View :

 struct ContentView: View { var body: some View { Text("Hello World") } } 

Il s'agit d'une déclaration SwiftUI du contenu d'un ContentView ( corps ). Il y a maintenant une vue texte avec le texte Hello World.

Juste en dessous de ContentView_Previews renvoie une instance de ContentView.

 struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } 

Ici, nous pouvons définir les données de test pour l'aperçu. Mais où est exactement cet aperçu?

Immédiatement après le code est un grand espace vide avec ceci ci-dessus:



Cliquez sur Reprendre , attendez un instant et ...



Esquissez notre interface utilisateur


Quelque chose de familier n'est pas visible - il s'agit du fichier Main.storyboard . Nous allons créer notre interface utilisateur en utilisant SwiftUI, directement dans le code, en train de regarder l'aperçu: qu'est-ce que nous y arrivons? Mais ne vous inquiétez pas, nous n'avons pas à écrire des centaines de lignes de code pour créer nos vues.

SwiftUI est déclaratif: vous indiquez à quoi devrait ressembler votre interface utilisateur, et SwiftUI convertit tout cela en un code efficace qui fait tout le travail. Apple vous permet de créer autant de vues que nécessaire afin que le code soit simple et direct. Les vues réutilisables avec des paramètres sont particulièrement recommandées - cela est similaire à l'allocation de code dans une fonction distincte. Vous le ferez vous-même plus tard.

Notre application aura beaucoup de vues, donc pour commencer, nous allons esquisser les vues de texte sous forme de talons.

Remplacez Text ("Hello World") par ceci:

 Text("Target Color Block") 

Si nécessaire, cliquez sur Reprendre pour mettre à jour l'aperçu.

Maintenant, Commandez-cliquez sur cette vue dans l' aperçu , puis sélectionnez Intégrer dans HStack :



Veuillez noter que votre code a également changé:

 HStack { Text("Target Color Block") } 

Copiez et collez l'opérateur Text et modifiez-le dans notre HStack. Veuillez noter: nous ne séparons pas les opérateurs par une virgule, mais écrivons chacun d'eux sur une nouvelle ligne:

 HStack { Text("Target Color Block") Text("Guess Color Block") } 

Et donc cela ressemble à l'aperçu:



Maintenant, préparons un emplacement pour les talons de curseur en plaçant le HStack sur VStack . Cette fois, nous allons faire un clic de commande sur le HStack dans notre code :



Choisissez Intégrer dans VStack ; un nouveau code apparaîtra, mais l'aperçu ne changera pas. Un peu plus tard, nous ajouterons la vue sous les futurs blocs de couleur.

Ajoutez une nouvelle ligne immédiatement après le HStack, cliquez sur + dans la barre d'outils pour ouvrir la bibliothèque et faites glisser Vertical Stack vers une nouvelle ligne:



Comme prévu, le code et l'aperçu ont changé:



Terminez le travail sur le projet d'interface utilisateur, de sorte que tout ressemble à ceci:

 VStack { HStack { Text("Target Color Block") Text("Guess Color Block") } Text("Hit me button") VStack { Text("Red slider") Text("Green slider") Text("Blue slider") } } 

Dans le nouveau VStack, trois curseurs apparaîtront un peu plus tard, ainsi qu'un bouton entre les curseurs et les blocs de couleur.

Nous continuons à travailler sur l'interface utilisateur


Maintenant, pratiquons dans SwiftUI pour remplir un HStack contenant des blocs colorés:

 HStack { // Target color block VStack { Rectangle() Text("Match this color") } // Guess color block VStack { Rectangle() HStack { Text("R: xxx") Text("G: xxx") Text("B: xxx") } } } 

Chaque bloc de couleur a un rectangle. Le bloc de couleur cible (cible) a une vue Texte sous le rectangle et celui sélectionné (Devinez) a trois vues Texte. Un peu plus tard, nous remplacerons «xxx» par les valeurs réelles des curseurs.

Utilisation des variables '@State'


Dans SwiftUI, nous pouvons utiliser des variables régulières, mais si nous voulons que le changement de la variable affecte l'interface utilisateur, nous marquons des variables comme '@State' . Dans notre application, nous sélectionnons la couleur, de sorte que toutes les variables qui affectent la couleur sélectionnée soient des variables '@State'.

Ajoutez ces lignes à l'intérieur de la structure ContentView , avant la déclaration du corps :

 let rTarget = Double.random(in: 0..<1) let gTarget = Double.random(in: 0..<1) let bTarget = Double.random(in: 0..<1) @State var rGuess: Double @State var gGuess: Double @State var bGuess: Double 

Les valeurs de R, G et B sont comprises entre 0 et 1. Nous initialisons les valeurs souhaitées avec des valeurs aléatoires. Nous pourrions également initialiser les valeurs sélectionnées à 0,5, mais les laisser non initialisées pour l'instant pour montrer ce qui doit être fait dans ce cas.

Allons un peu plus bas dans la structure ContentView_Previews , qui initialise l'instance ContentView pour l'aperçu . Maintenant, l'initialiseur a besoin des valeurs initiales des valeurs sélectionnées. Modifiez ContentView () comme ceci:

 ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5) 

Lorsque nous faisons les curseurs, dans l'aperçu, leurs valeurs seront au milieu.

Nous devons également corriger l' initialiseur dans SceneDelegate , dans la fonction de scène (_: willConnectTo: options :) - remplacer ContentView () par ce qui suit:

 window.rootViewController = UIHostingController(rootView: ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5)) 

Lorsque l'application se charge, les pointeurs de curseur seront au centre.

Ajoutez maintenant un modificateur de couleur au rectangle cible:

 Rectangle() .foregroundColor(Color(red: rTarget, green: gTarget, blue: bTarget, opacity: 1.0)) 

Le modificateur .foregroundColor crée une nouvelle vue Rectangle avec la couleur spécifiée par les valeurs RVB générées aléatoirement.

De même, modifiez le rectangle de devinettes:

 Rectangle() .foregroundColor(Color(red: rGuess, green: gGuess, blue: bGuess, opacity: 1.0)) 

Avec des valeurs de R, G et B à 0,5, nous obtenons une couleur grise.

Cliquez sur Reprendre et attendez un instant.



Rendre la vue réutilisable


Tout d'abord, nous ne penserons pas à la réutilisation et ferons simplement un curseur pour le rouge. Dans VStack for sliders, remplacez la fiche Texte ("Red slider") par cette HStack :

 HStack { Text("0") .foregroundColor(.red) Slider(value: $rGuess) Text("255") .foregroundColor(.red) } 

Nous avons fait de la couleur du texte dans la vue texte rouge. Et ils ont ajouté Slider avec une valeur par défaut. La plage par défaut du curseur est de 0 à 1, c'est quelque chose qui nous convient parfaitement.
Remarque: Nous savons que le curseur passe de 0 à 1, et l'étiquette de texte est «255» pour la commodité des utilisateurs qui sont habitués à représenter des valeurs RVB dans la plage de 0 à 255.
Mais quel type d'icône $ la variable possède-t-elle? Le savons-nous ? et! lorsque vous travaillez avec des options , et maintenant aussi $ ?

Malgré le fait qu'il soit si petit et discret, il est très important. RGuess lui-même n'est qu'une valeur en lecture seule. Mais $ rGuess est une liaison , nous en avons besoin pour mettre à jour le rectangle de la couleur sélectionnée lorsque l'utilisateur déplace le curseur.

Pour comprendre la différence, définissez les valeurs des trois vues de texte sous le rectangle prévisible:

 HStack { Text("R: \(Int(rGuess * 255.0))") Text("G: \(Int(gGuess * 255.0))") Text("B: \(Int(bGuess * 255.0))") } 

Ici, nous utilisons uniquement les valeurs, ne les modifions pas, nous n'avons donc pas besoin du préfixe $.

Attendez la mise à jour de l'aperçu:



Les rectangles colorés ont légèrement rétréci pour s'adapter au curseur. Mais les étiquettes de texte du curseur semblent désordonnées - elles sont trop pressées contre les bords. Ajoutons un autre modificateur à HStack - padding:

 HStack { Text("0") .foregroundColor(.red) Slider(value: $rGuess) Text("255") .foregroundColor(.red) } .padding() 

Maintenant beaucoup mieux!



Cliquez sur le curseur rouge HStack et sélectionnez Extraire la sous-vue :



Cela fonctionne de la même manière que Refactor ▸ Extract to Function , mais pour la vue SwiftUI.

À ce stade, plusieurs messages d'erreur apparaîtront, ne vous inquiétez pas, nous allons maintenant le corriger.

Nommez la vue résultante ColorSlider et ajoutez ce code en haut, devant le corps de notre nouvelle vue:

 @Binding var value: Double var textColor: Color 

Remplacez maintenant $ rGuess par $ value et .red par textColor :

 Text("0") .foregroundColor(textColor) Slider(value: $value) Text("255") .foregroundColor(textColor) 

Revenons à la définition de ColorSlider () dans VStack et ajoutons nos paramètres:

 ColorSlider(value: $rGuess, textColor: .red) 

Assurez-vous que l'aperçu est OK avec le curseur rouge et remplacez les talons de texte par les curseurs vert et bleu. N'oubliez pas d'y insérer les paramètres corrects:

 ColorSlider(value: $gGuess, textColor: .green) ColorSlider(value: $bGuess, textColor: .blue) 

Cliquez sur Reprendre pour mettre à jour l'aperçu:


Remarque: vous avez peut-être remarqué que vous devez souvent cliquer sur Reprendre . Si vous aimez les raccourcis, vous adorerez probablement Option-Command-P .
Et maintenant quelque chose de sympa! Dans le coin inférieur droit de l'aperçu, cliquez sur le bouton d' aperçu en direct :



L'aperçu en direct nous permet d'interagir avec l'aperçu comme si l'application s'exécutait sur un simulateur!

Essayez de déplacer les curseurs:



Super! Nous passons à l'étape finale. Après tout, nous voulons savoir dans quelle mesure nous avons choisi la couleur?

Afficher l'alerte


Après avoir réglé les curseurs aux positions souhaitées, l'utilisateur appuie sur le bouton Hit Me , après quoi Alert apparaît avec une note.

Tout d'abord, ajoutez une méthode à ContentView pour calculer le score. Entre les variables @State et
corps ajouter cette méthode:

 func computeScore() -> Int { let rDiff = rGuess - rTarget let gDiff = gGuess - gTarget let bDiff = bGuess - bTarget let diff = sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff) return Int((1.0 - diff) * 100.0 + 0.5) } 

La valeur diff est simplement la distance entre deux points dans un espace tridimensionnel, c'est-à-dire la valeur de l'erreur de l'utilisateur. Pour obtenir une estimation, soustrayez diff de 1, puis réduisez sa valeur dans la plage 0 - 100. Plus la différence est petite, plus l'estimation est élevée.

Remplacez ensuite le talon Text ("Hit me button") par ce code:

 Button(action: { }) { Text("Hit Me!") } 

Le bouton a une action et une étiquette, comme UIButton. Nous voulons que l'action déclenche une vue d'alerte. Mais, si nous créons une alerte dans le bouton d'action, rien ne se passera.

Au lieu de cela, nous intégrerons Alert à ContentView et ajouterons une variable '@State' de type Bool. Ensuite, nous définissons cette variable sur true, à l'endroit où nous voulons que notre alerte apparaisse dans le bouton d'action. La valeur sera réinitialisée à false - pour masquer l'alerte - lorsque l'utilisateur masque Alert.

Ajoutez cette variable '@State':

 @State var showAlert = false 

Ajoutez ensuite ce code comme bouton d'action:

 self.showAlert = true 

l'auto est nécessaire pour nous, puisque showAlert est à l'intérieur de la fermeture.

Enfin, ajoutez le modificateur d'alerte au bouton, afin que notre bouton ressemble complètement à ceci:

 Button(action: { self.showAlert = true }) { Text("Hit Me!") } .alert(isPresented: $showAlert) { Alert(title: Text("Your Score"), message: Text("\(computeScore())")) } 

Nous passons $ showAlert en tant que liaison, car la valeur de cette variable changera au moment où l'utilisateur masque l'alerte, et cette modification entraînera la mise à jour de la vue.

SwiftUI a un simple initialiseur pour la vue Alert. Il a un bouton OK par défaut, nous n'avons donc même pas besoin de le définir comme paramètre.

Activez l'aperçu en direct, déplacez les curseurs et appuyez sur le bouton Hit me. Voila!



Maintenant, avec l'aperçu en direct, vous n'avez plus besoin d'un simulateur iOS. Bien qu'avec elle, vous pouvez tester votre application horizontalement:



Conclusion


Ici vous pouvez télécharger le projet de publication terminé.

Ce didacticiel ne couvre que légèrement SwiftUI, mais vous avez maintenant une idée des nouvelles fonctionnalités Xcode pour créer des interfaces utilisateur et des aperçus, et comment utiliser les variables '@State' pour mettre à jour votre interface utilisateur.

Par souci de simplicité, nous n'avons pas créé de modèle de données pour RVB. Mais la plupart des applications créent des modèles de leurs données à l'aide de structures ou de classes. Si vous souhaitez suivre les modifications du modèle dans SwiftUI, il doit se conformer au protocole ObservableObject et implémenter la propriété willChange , qui vous informe des modifications. Consultez les exemples Apple et en particulier le flux de données via SwiftUI .

Pour faciliter la compréhension de SwiftUI, vous pouvez ajouter une vue SwiftUI à une application existante. Consultez des exemples sur la façon de procéder rapidement et facilement.

Voir ma publication sur l'implémentation des listes pliantes / déroulantes à l'aide de SwiftUI.

Enfin, étudiez la documentation de SwiftUI , et il y a vraiment beaucoup de choses utiles!

Source: https://habr.com/ru/post/fr471918/


All Articles