Image de présentation d’un écran iOS illustrant les shapes en SwiftUI, plusieurs forme de différentes couleurs sont présentes

@Binding

Image de présentation d’un écran iOS illustrant les shapes en SwiftUI, plusieurs forme de différentes couleurs sont présentes

@Binding

C’est quoi @Binding ?

Dans le cours précédent, on a vu comment créer un composant réutilisable. Jusqu’ici, on a isolé notre composant, défini des propriétés pour personnaliser ses valeurs lors de l’instance, puis appelé cette vue isolée dans d’autres vues. Mais que se passe-t-il si l’on veut que ce composant puisse modifier une valeur extérieure, qui ne lui appartient pas ? Prenons un exemple, je veux créer un bouton réutilisable que je vais appeler dans une autre vue. Ce bouton doit pouvoir modifier un @State de la vue dans laquelle il est utilisé. Problème Un @State appartient uniquement à la vue qui l’a créé. Cela signifie que mon composant ne peut pas modifier directement le @State de la vue parent dans laquelle il est instancié. Solution C’est là que @Binding intervient. Ce property wrapper permet à une vue enfant de modifier une valeur détenue par sa vue parent, tout en respectant la gestion des données en SwiftUI. Grâce à @Binding, mon composant peut interagir avec une valeur extérieure sans en être propriétaire.

Fonctionnement de @Binding

Pour introduire le @Binding je vais faire comme dans le cours précédent, je vais te montrer d’abord la problématique et ensuite la solution.


struct ChangeColorView: View {
    @State var myColor = Color.orange
    var body: some View {
        ZStack {
            Color(myColor)
            Button {
                myColor = myColor == .orange ? .green : .orange
            } label: {
                ZStack{
                    Rectangle()
                        .fill(Color.black)
                        .cornerRadius(12)
                        .frame(width: 150, height: 50)
                    Text("\(myColor == .orange ? "Vert" : "Orange")")<br />                        .font(.headline)<br />                        .foregroundStyle(Color.white)
                        .fontWeight(.black)
                        .fontWidth(.expanded)
                }
            }
        }
        .ignoresSafeArea()
    }
}

Je viens de créer une vue qui me permet de changer la couleur du fond d’écran grâce à un bouton qui redéfinit la couleur de la propriété @State myColor lors de son exécution.

 

Je vais maintenant extraire le bouton :

 


struct ChangeColorView: View {
    @State var myColor = Color.orange
    var body: some View {
        ZStack {
            Color(myColor)
            BtnColorView()
        }
        .ignoresSafeArea()
    }
}

struct BtnColorView: View {
    var body: some View {
        Button {
            myColor = myColor == .orange ? .green : .orange // ❌ Cannot find 'myColor' in scope
        } label: {
            ZStack{
                Rectangle()
                    .fill(Color.black)
                    .cornerRadius(12)
                    .frame(width: 150, height: 50)
                Text("\(myColor == .orange ? "Vert" : "Orange")") // ❌ Cannot find 'myColor' in scope<br />                    .font(.headline)<br />                    .foregroundStyle(Color.white)
                    .fontWeight(.black)
                    .fontWidth(.expanded)
            }
        }
    }
}

La problématique se pose ici, j’ai isolé le bouton, mais celui-ci a besoin de la propriété @State myColor, qui se trouve dans la vue ChangeColorView. Je n’y ai donc pas accès directement.

 

La solution @Binding

La solution consiste donc à créer une propriété @Binding dans la vue enfant, avec le même type que la propriété @State dans la vue parent. Ça permettra de faire le lien entre les deux propriétés lorsque j’instancierai mon bouton. Je te montre :

struct ChangeColorView: View {
    @State var myColor = Color.orange
    var body: some View {
        ZStack {
            Color(myColor)
            BtnColorView(myColorBinding: $myColor)
        }
        .ignoresSafeArea()
    }
}

struct BtnColorView: View {
    @Binding var myColorBinding: Color
    var body: some View {
        Button {
            myColorBinding = myColorBinding == .orange ? .green : .orange
        } label: {
            ZStack{
                Rectangle()
                    .fill(Color.black)
                    .cornerRadius(12)
                    .frame(width: 150, height: 50)
                Text("\(myColorBinding == .orange ? "Vert" : "Orange")")<br />                    .font(.headline)<br />                    .foregroundStyle(Color.white)
                    .fontWeight(.black)
                    .fontWidth(.expanded)
            }
        }
    }
}

Dans cet exemple, la vue parent (ChangeColorView) détient la variable @State var myColor = Color.orange, qui représente la couleur de fond de l’écran. Étant donné que cette variable est déclarée avec @State, elle appartient exclusivement à cette vue et ne peut être modifiée directement par une autre vue.

Cependant, la vue enfant (BtnColorView) doit pouvoir modifier cette couleur lorsqu’on appuie sur le bouton. Pour cela, au lieu de lui passer simplement une copie de la couleur, on lui transmet une référence sous forme de @Binding.

Lorsqu’on instancie BtnColorView dans la vue parent, on écrit :


BtnColorView(myColorBinding: $myColor)

Le $ devant myColor permet de convertir @State var myColor en Binding, ce qui signifie que la vue enfant ne reçoit pas une valeur fixe, mais un accès direct à la variable d’origine.

Dans la vue enfant (BtnColorView), la couleur est récupérée sous forme de @Binding :

 


@Binding var myColorBinding: Color

Ça permet au bouton d’écrire directement dans myColorBinding, ce qui mettra à jour myColor dans la vue parent. Grâce à cette liaison, la modification se propage automatiquement dans toute l’interface sans intervention manuelle.

En résumé, le @Binding permet de partager un état entre une vue parent et une vue enfant, et le $ est essentiel lorsqu’on passe un @State en @Binding, car il convertit la variable en une référence modifiable au lieu d’une simple copie.

Tips

En général, cette notion peut sembler compliquée la première fois qu’on s’y confronte, mais avec un peu de recul, c’est en réalité très simple.

Le @Binding permet simplement de créer un lien entre une vue extraite et les @State des vues qui l’appellent. C’est tout !

Si cela te paraît encore abstrait, je t’invite à reproduire les exemples du cours et surtout à pratiquer. Tu trouveras un exercice sur le sujet à la suite de ce cours.

Tips

En général, cette notion peut sembler compliquée la première fois qu’on s’y confronte, mais avec un peu de recul, c’est en réalité très simple.

Le @Binding permet simplement de créer un lien entre une vue extraite et les @State des vues qui l’appellent. C’est tout !

Si cela te paraît encore abstrait, je t’invite à reproduire les exemples du cours et surtout à pratiquer. Tu trouveras un exercice sur le sujet à la suite de ce cours.