Je travaille sur un écran de saisie d'argent et j'ai besoin d'implémenter un init
personnalisé pour définir une variable d'état basée sur le montant initialisé.
Je pensais que cela fonctionnerait, mais j'obtiens une erreur de compilation de:
Cannot assign value of type 'Binding<Double>' to type 'Double'
struct AmountView : View {
@Binding var amount: Double
@State var includeDecimal = false
init(amount: Binding<Double>) {
self.amount = amount
self.includeDecimal = round(amount)-amount > 0
}
...
}
Argh! Tu étais si proche. Voilà comment vous le faites. Vous avez manqué un signe dollar (bêta 3) ou un trait de soulignement (bêta 4), et vous vous êtes placé devant votre propriété de montant, ou .valeur après le paramètre de montant. Toutes ces options fonctionnent:
Vous verrez que j'ai supprimé le @State
in includeDecimal, vérifiez l'explication à la fin.
Ceci utilise la propriété (mettez-vous devant elle):
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(amount: Binding<Double>) {
// self.$amount = amount // beta 3
self._amount = amount // beta 4
self.includeDecimal = round(self.amount)-self.amount > 0
}
}
ou en utilisant .value après (mais sans self, car vous utilisez le paramètre passé, pas la propriété de la structure):
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(amount: Binding<Double>) {
// self.$amount = amount // beta 3
self._amount = amount // beta 4
self.includeDecimal = round(amount.value)-amount.value > 0
}
}
C'est la même chose, mais nous utilisons des noms différents pour le paramètre (withAmount) et la propriété (amount), donc vous voyez clairement quand vous les utilisez. =
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(withAmount: Binding<Double>) {
// self.$amount = withAmount // beta 3
self._amount = withAmount // beta 4
self.includeDecimal = round(self.amount)-self.amount > 0
}
}
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(withAmount: Binding<Double>) {
// self.$amount = withAmount // beta 3
self._amount = withAmount // beta 4
self.includeDecimal = round(withAmount.value)-withAmount.value > 0
}
}
Notez que .value n'est pas nécessaire avec la propriété, grâce au wrapper de propriété (@Binding), qui crée les accesseurs qui rendent la .value inutile. Cependant, avec le paramètre, il n'y a rien de tel et vous devez le faire explicitement. Si vous souhaitez en savoir plus sur les wrappers de propriétés, consultez WWDC session 415 - Modern Swift API Design et passez à 23:12.
Comme vous l'avez découvert, la modification de la variable @State à partir de l'initiateur générera l'erreur suivante: Thread 1: erreur fatale: accès à l'état en dehors de View.body. Pour l'éviter, vous devez soit supprimer @State. Ce qui est logique car includeDecimal n'est pas une source de vérité. Sa valeur est dérivée du montant. Cependant, en supprimant @State, includeDecimal
ne sera pas mis à jour si le montant change. Pour y parvenir, la meilleure option consiste à définir votre includeDecimal en tant que propriété calculée, de sorte que sa valeur soit dérivée de la source de vérité (quantité). De cette façon, chaque fois que le montant change, votre includeDecimal le fait aussi. Si votre vue dépend de includeDecimal, elle devrait se mettre à jour lorsqu'elle change:
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal: Bool {
return round(amount)-amount > 0
}
init(withAmount: Binding<Double>) {
self.$amount = withAmount
}
var body: some View { ... }
}
Comme indiqué par rob mayoff, vous pouvez également utiliser $$varName
(beta 3) ou _varName
(beta4) pour initialiser une variable d'état:
// Beta 3:
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
// Beta 4:
_includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
Vous avez dit (dans un commentaire) "Je dois pouvoir changer includeDecimal
". Que signifie changer includeDecimal
? Vous voulez apparemment l'initialiser selon que amount
(au moment de l'initialisation) est un entier. D'accord. Que se passe-t-il donc si includeDecimal
est false
et que vous le changez ensuite en true
? Allez-vous forcer d'une manière ou d'une autre amount
à être non entier?
Quoi qu'il en soit, vous ne pouvez pas modifier includeDecimal
dans init
. Mais vous pouvez l'initialiser dans init
, comme ceci:
struct ContentView : View {
@Binding var amount: Double
init(amount: Binding<Double>) {
$amount = amount
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
}
@State private var includeDecimal: Bool
(Notez que à un moment donné le $$includeDecimal
la syntaxe sera remplacée par _includeDecimal
.)