La possibilité de créer des modificateurs de vue personnalisés est une fonctionnalité puissante de SwiftUI. Dans cet article, nous couvrirons des exemples de la façon dont cette fonctionnalité peut être utilisée pour rendre la création de l'interface utilisateur beaucoup plus facile. Si vous n'êtes pas familier avec les ViewModifiers dans SwiftUI et comment en créer des personnalisés, vous pouvez en savoir plus ici
Le but de cet article est de couvrir certaines des différentes façons de créer des modificateurs et des styles personnalisés dans SwiftUI et comment ils peuvent être utilisés pour rendre la construction de l'interface utilisateur plus déclarative tout en obtenant une sortie finale propre et cohérente.
L'interface utilisateur finale que nous voulons créer est la suivante :
Considérons tous les composants individuels à l'écran :
Si vous créez cet écran sans aucun modificateur, le code ressemblera à ceci :
struct ContentView: View { var body: some View { VStack (alignment: .leading) { Image("feature") .resizable() .aspectRatio(contentMode: .fill) .frame(minWidth: 0, maxWidth: .infinity) .frame(height: 220) .cornerRadius(12) .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI are the best!") .foregroundColor(Color("titleTextColor")) .font(.system(size: 20, weight: .bold)) .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI let you create resuable styles that can be applied to all your views") .foregroundColor(Color("bodyTextColor")) .font(.system(size: 14, weight: .medium)) Spacer() Button(action: { }) { Text("Label") .font(.system(size: 14, weight: .medium)) } .frame(minWidth: 0, maxWidth: .infinity) .padding(.horizontal, 10) .padding(.vertical, 12) .background(Color.blue) .foregroundColor(Color.white) .cornerRadius(12) } .padding(.all, 16) } }
Le style de certains éléments (le texte du titre et des détails par exemple) devrait être dupliqué
Des modifications à certains des styles courants (remplissage d'élément, rayon d'angle, etc.) devraient être apportées à plusieurs endroits
Maintenant, vous pouvez résoudre ce problème à la manière UIKit en créant des vues personnalisées, mais je ne suis pas fan de cette approche car elle impliquait de s'éloigner des vues intégrées et rend l'intégration de nouveaux membres de l'équipe plus frictionnelle. Un moyen plus simple serait de définir des modificateurs de vue universels pouvant être appliqués à la place des styles eux-mêmes.
Décomposons le style commun dont nous avons besoin :
Commençons par le rayon du coin :
struct CommonCornerRadius: ViewModifier { func body(content: Content) -> some View { content .cornerRadius(12) } }
Celui-ci est assez simple, il nous permet d'appliquer un rayon de coin universel pour les éléments. Cela facilite la modification globale des styles d'application sans avoir à créer des vues personnalisées ou à effectuer plusieurs modifications dans la base de code.
struct FullWidthModifier: ViewModifier { func body(content: Content) -> some View { content .frame(minWidth: 0, maxWidth: .infinity) } }
Celui-ci facilite la mise en œuvre des vues pleine largeur, plus besoin d'ajouter manuellement .frame
!
struct TitleTextModifier: ViewModifier { func body(content: Content) -> some View { content .foregroundColor(Color("titleTextColor")) .font(.system(size: 20, weight: .bold)) } } struct BodyTextModifier: ViewModifier { func body(content: Content) -> some View { content .foregroundColor(Color("bodyTextColor")) .font(.system(size: 14, weight: .medium)) } }
Cela permettra un style de texte commun, normalement vous créerez des composants de texte personnalisés ou des fonctions utilitaires et ajouterez des composants d'interface utilisateur via le code.
extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }
D'accord, vous m'avez compris… ce n'est pas un modificateur de vue personnalisé mais une simple extension. En effet, ViewModifiers s'applique aux vues génériques et certaines fonctions telles que resizable
ne s'appliquent qu'aux images, l'utilisation d'une combinaison d'extensions et de modificateurs personnalisés permet de contourner ce problème.
struct FullWidthButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .fullWidth() .foregroundColor(Color.white) .font(.system(size: 14, weight: .medium)) .padding(.horizontal, 10) .padding(.vertical, 12) .background(configuration.isPressed ? Color.blue.opacity(0.2) : Color.blue) } } struct FullWidthButton: ViewModifier { func body(content: Content) -> some View { content .buttonStyle(FullWidthButtonStyle()) } }
Enfin, c'est pour le bouton, notez que même si nous aurions pu simplement créer un ViewModifier pour obtenir le même effet, l'apparence du bouton n'aurait pas changé lorsqu'il aurait été tapé. En effet, la définition de .background
sur un bouton l'oblige à utiliser cet arrière-plan dans les états activé et non activé. ButtonStyle
nous permet de modifier l'opacité du bouton selon qu'il est enfoncé ou non.
Maintenant, pour plus de commodité, j'aime créer des extensions qui utilisent ces modificateurs :
extension View { func commonCornerRadius() -> some View { modifier(CommonCornerRadius()) } func fullWidth() -> some View { modifier(FullWidthModifier()) } func title() -> some View { modifier(TitleTextModifier()) } func body() -> some View { modifier(BodyTextModifier()) } func fullWidthButton() -> some View { modifier(FullWidthButton()) } } extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }
struct ContentView: View { var body: some View { VStack (alignment: .leading) { Image("feature") .aspectFill() .fullWidth() .frame(height: 220) .commonCornerRadius() .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI are the best!") .title() .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI let you create resuable styles that can be applied to all your views") .body() Spacer() Button(action: { }) { Text("Awesome") } .fullWidthButton() .commonCornerRadius() } .padding(.all, 16) } }
Beaucoup plus propre ! Maintenant, à première vue, cela ressemble à plus de code et d'efforts que de simplement définir manuellement les styles, mais à long terme, cela économisera beaucoup d'efforts. Personnellement, cette approche encourage également le style de votre application à être plus cohérent en s'appuyant davantage sur des modificateurs courants que sur une base de style vue par vue.
Et c'est tout ! Espérons que cela vous aide à créer vos applications plus rapidement et plus facilement, un autre avantage est que ces modificateurs peuvent être déposés dans n'importe laquelle de vos applications et modifiés pour correspondre à ses directives de style. J'ai également travaillé sur une bibliothèque pour aller encore plus loin, vous pouvez la consulter ici (PS : Au moment d'écrire ces lignes, la bibliothèque est à un stade très précoce, le référentiel est vide : p mais restez à l'écoute)
Également publié ici.