Lors de la WWDC 2019 qui s’est tenue le 3 juin, Apple a dévoilé son nouveau framework SwiftUI permettant d’implémenter des interfaces en Swift de façon déclarative respectant les “Human Interface Guidelines“, simplifiant tous les patterns récurrents dans les applis iOS.
Le nouveau framework nécessitera donc iOS 13+ pour tourner. Il faudra donc attendre au moins 2 ans pour que ce dernier soit adopté par les développeurs puisqu’en moyenne une application iOS supporte les 2 dernières versions de l’OS.
En quelques semaines, beaucoup de développeurs se sont intéressés à SwiftUI et donc plein de projets sont déjà disponibles en Open Source. Voici un repository répertoriant les plus intéressants : About-SwiftUI
Objectif :
Nous vous proposons dans cet article de voir quelques unes des possibilités offertes par SwiftUI en implémentant la page d’accueil d’Instagram.
Pré-requis :
- macOS Mojave + ou macOS Catalina.
- Xcode 11.
que vous pouvez télécharger ici.
NB: le live preview sur Xcode 11 n’est disponible que sur MacOS Catalina !
Vous pouvez également télécharger l’ensemble des Assets utilisés dans ce tutoriel ici.
L’ application :
Commençons par créer un projet Xcode. Assurez vous que la case “Use SwiftUI” est bien cochée.
Une fois votre projet créé, vous devez avoir 3 fichiers Swift :
- AppDelegate.swift
- SceneDelegate.swift
- ContentView.swift
Vous remarquerez la création du nouveau Scene Delegate avec l’arrivée du Scene-Based Life-Cycle.
En ce qui nous concerne, nous allons nous intéresser au fichier ContentView.swift qui contiendra un Hello World en SwiftUI :
1 2 3 4 5 |
struct ContentView: View { var body: some View { Text("Hello World") } } |
quant à :
1 2 3 4 5 6 7 |
#if DEBUG struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } #endif |
C’est ce qui permet d’avoir un live preview de votre code sur Xcode (si vous utilisez MacOS Catalina).
Commençons par créer une TableView. En SwiftUI cela se fait en une ligne :
1 2 3 4 5 6 7 |
struct ContentView: View { var body: some View { List { Text("Hello World") } } } |
Construisons notre PostCell qui contiendra un post. La cell peut être subdivisée en :
- Header : contenant l’utilisateur qui a posté.
- Post : l’image.
- Barre horizontale : contenant les boutons de like, comment, share et save.
- Le nombre de Likes.
- La description.
Si on décompose le Header on aura :
Avec en vert une HStack (Horizontal Stack) et en bleu une VStack (Vertical Stack)
En SwiftUI ça donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct PostCell: View { var body: some View { HStack { Image("Avatar") VStack(alignment: .leading){ Text("pieroborgo") Text("Milan, Italy") } Spacer() Image("More") } } } |
(N’oubliez pas de mettre à jour votre ContentView en remplaçant Text(“Hello World”) par une instance PostCell() )
On ajustera la taille du texte :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct PostCell: View { var body: some View { HStack { Image("Avatar") VStack(alignment: .leading){ Text("pieroborgo") .font(Font.system(size: 13.5)) Text("Milan, Italy") .font(Font.system(size: 11.5)) } Spacer() Image("More") } } } |
Passons à la suite de la PostCell. Le concept reste le même, subdiviser la view en HStack et en VStack.
Ce qui donne en SwiftUI:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
struct PostCell: View { var body: some View { VStack { // Header HStack { Image("Avatar") VStack(alignment: .leading){ Text("pieroborgo") .font(Font.system(size: 13.5)) Text("Milan, Italy") .font(Font.system(size: 11.5)) } Spacer() Image("More") } // Post Image("Photo") .resizable() // Barre horizontale HStack(alignment: .center) { Image("Like") Image("Comment") Image("Send") Spacer() Image("Collect") } // Le nombre de Likes Text("Liked by leeviahq and 621 others") .font(Font.system(size: 13.5)) // La description Text("pieroborgo Thanks for dowloading this freebie ❤️ #freebie #instagram #sketch") .lineLimit(4) .font(Font.system(size: 12.5)) .foregroundColor(.init(white: 0.2)) } } } |
NB : .lineLimit( Int? ) : permet de préciser le nombre maximal de lignes pour un Text. (nil pour un nombre illimité de lignes)
On s’approche du résultat souhaité mais on a encore quelques petits problèmes :
- L’image ne prend pas toute la largeur de l’écran.
- Le nombre de likes est centré.
Le comportement de l’image est voulu par Apple puisque ça respecte les Guidelines concernant la SafeArea. Mais dans notre cas cela ne nous convient pas. Il suffit donc de donner à l’image un leading et un trailing padding négatifs correspondant à la taille de la SafeArea (20 px).
1 2 3 4 5 |
Image("Photo") .resizable() .scaledToFit() .padding(.leading, -20) .padding(.trailing, -20) |
En ce qui concerne le second problème, il faut savoir que les HStack, VStack et ZSatck prennent un paramètre optionnel pour l’alignement. Il suffit donc de spécifier un alignement .leading pour la VStack englobant toutes nos Views:
1 2 3 |
VStack(alignment: .leading){ // code } |
Ajoutons maintenant une barre de navigation et quelques cellules :
1 2 3 4 5 6 7 8 9 10 11 |
struct ContentView: View { var body: some View { NavigationView { List { PostCell() PostCell() PostCell() }.navigationBarTitle("InstaUI", displayMode: .inline) } } } |
Passons aux Stories. Il s’agit tout simplement d’une ScrollView horizontale contenant une HStack.
On garde la même notation : HStack en vert et VStack en bleu, avec une ScrollView en rouge :
On crée une nouvelle struct dédiée au Stories :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
struct StoriesView: View { var body: some View { VStack(alignment: .leading) { // Stories text + Watch all HStack { Text("Stories") Spacer() Image("Watch") Text("Watch all") } // Stories Circles ScrollView([.horizontal], showsIndicators: false){ HStack { Image("AvatarBig1") Image("AvatarBig1") } } } } } |
Les Stories sont désormais Scrollable horizontalement.
(N’oubliez pas d’ajouter une instance StoriesView() dans List de votre ContentView)
On veut maintenant ajouter l’image de bordure en arrière plan. On utilisera donc une ZStack :
1 2 3 4 |
ZStack { Image("Border") Image("AvatarBig1") } |
Mettons cela dans une VStack afin d’ajouter le nom de l’utilisateur en dessous de sa photo de profil :
1 2 3 4 5 6 7 8 9 10 |
VStack { ZStack { Image("Border") Image("AvatarBig1") } Text("beardguy") .font(Font.system(size: 13.5)) } |
On s’approche du résultat final. Toutefois, à cause de la SafeArea, la ScrollView ne prend pas toute la largeur de l’écran (comme on a vu précédemment pour l’image de la PostCell). On ajoute donc un leading et un trailing padding avec des valeurs négatives à la ScrollView et un petit padding à la HStack pour compenser.
1 2 3 4 5 6 7 8 9 10 11 12 |
ScrollView { HStack { VStack { }.padding(.trailing, 12) // espace entre cellules }.padding(.leading, 16) // espace au début de la ligne }.padding(.leading, -14) .padding(.trailing, -14) // prendre toute la largeur de l'écran |
Pour finir la partie Stories, il ne manque plus que le “Your Story” avec le petit plus bleu. Il ne s’agit que d’une ZStack avec bottomTrailing comme Alignment.
On ajoute donc au début de notre HStack qui contient les cellules de nos Stories :
1 2 3 4 5 6 7 8 |
VStack { ZStack(alignment: .bottomTrailing) { Image("AvatarBig") Image("Add") } Text("Your Story") .font(Font.system(size: 13.5)) }.padding(.trailing, 12) |
Et on obtient :
Ajoutons maintenant les deux boutons dans la Navigation Bar :
1 2 3 4 5 6 7 |
List { StoriesView() PostCell() PostCell() PostCell() }.navigationBarTitle("InstaUI", displayMode: .inline) .navigationBarItems(leading: Image("Camera"), trailing: Image("Direct")) |
L’application est disponible en Open Source sur ce dépôt GitHub et pourra évoluer.
Conclusion :
Pour l’avoir testé pendant quelques jours SwiftUI est un Framework assez puissant et efficace surtout pour des UI simples. Plus besoin de UITableViewDelegates, ViewController management… Cependant UIKit restera le framework phare d’Apple (pour le moment). Sachez qu’il est possible d’utiliser les deux Frameworks UIKit et SwiftUI au sein du même projet.
N’hésitez pas à jeter un coup d’oeil sur quelques projets intéressants réalisés en SwiftUI : About-SwiftUI
|
0commentaire(s)