Argh! Você estava tão perto. É assim que se faz. Você perdeu um cifrão (beta 3) ou sublinhado (beta 4), e mesmo na frente de sua propriedade de valor, ou .value após o parâmetro de valor. Todas essas opções funcionam:
Você verá que removi o @State
in includeDecimal, verifique a explicação no final.
Isso é usar a propriedade (coloque-se na frente dela):
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(amount: Binding<Double>) {
self._amount = amount
self.includeDecimal = round(self.amount)-self.amount > 0
}
}
ou usando .value after (mas sem self, porque você está usando o parâmetro passado, não a propriedade da estrutura):
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(amount: Binding<Double>) {
self._amount = amount
self.includeDecimal = round(amount.value)-amount.value > 0
}
}
É o mesmo, mas usamos nomes diferentes para o parâmetro (withAmount) e a propriedade (amount), para que você veja claramente quando está usando cada um.
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(withAmount: Binding<Double>) {
self._amount = withAmount
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
self.includeDecimal = round(withAmount.value)-withAmount.value > 0
}
}
Observe que .value não é necessário com a propriedade, graças ao wrapper de propriedade (@Binding), que cria os acessadores que tornam o .value desnecessário. Porém, com o parâmetro, não existe tal coisa e você tem que fazer isso explicitamente. Se você quiser saber mais sobre os wrappers de propriedade, verifique a sessão WWDC 415 - Modern Swift API Design e pule para 23:12.
Como você descobriu, modificar a variável @State do initilizer gerará o seguinte erro: Thread 1: Erro fatal: Accessing State outside View.body . Para evitá-lo, você deve remover o @State. O que faz sentido porque includeDecimal não é uma fonte de verdade. Seu valor é derivado da quantidade. Ao remover @State, no entanto, includeDecimal
não será atualizado se a quantidade mudar. Para isso, a melhor opção é definir seu includeDecimal como uma propriedade computada, de forma que seu valor seja derivado da fonte da verdade (quantidade). Dessa forma, sempre que o valor muda, seu includeDecimal também muda. Se sua visualização depende de includeDecimal, ela deve ser atualizada quando mudar:
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 { ... }
}
Conforme indicado por rob mayoff , você também pode usar $$varName
(beta 3) ou _varName
(beta4) para inicializar uma variável de estado:
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
_includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
self.includeDecimal = round(self.amount)-self.amount > 0
deThread 1: Fatal error: Accessing State<Bool> outside View.body
@State
variáveis devem representar uma fonte de verdade. Mas, no seu caso, você está duplicando essa verdade, porque o valor de includeDecimal pode ser derivado de sua fonte real de verdade, que é amount. Você tem duas opções: 1. Você torna includeDecimal uma var privada (sem @State), ou ainda melhor 2. Você torna uma propriedade computada que deriva seu valor deamount
. Dessa forma, se a quantidade mudar,includeDecimal
também muda . Você deve declará-lo assim:private var includeDecimal: Bool { return round(amount)-amount > 0 }
e remover oself.includeDecimal = ...
includeDecimal
então preciso disso como uma variável @State na visualização. Eu realmente quero inicializá-lo com um valor inicial.value
foi substituído por.wrappedValue
, seria bom atualizar a resposta e remover as opções beta.Você disse (em um comentário) “Eu preciso ser capaz de mudar
includeDecimal
”. O que significa mudarincludeDecimal
? Aparentemente, você deseja inicializá-lo com base em seamount
(no momento da inicialização) é um número inteiro. OK. Então, o que acontece seincludeDecimal
forfalse
e depois você muda paratrue
? Você vai forçar de alguma formaamount
a ser um não inteiro?De qualquer forma, você não pode modificar
includeDecimal
eminit
. Mas você pode inicializá-loinit
, assim: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
(Observe que em algum momento a
$$includeDecimal
sintaxe será alterada para_includeDecimal
.)fonte
Já que estamos em meados de 2020, vamos recapitular:
Quanto a
@Binding amount
_amount
só é recomendado para ser usado durante a inicialização. E nunca atribua desta formaself.$amount = xxx
durante a inicializaçãoamount.wrappedValue
eamount.projectedValue
não são usados com frequência, mas você pode ver casos como@Environment(\.presentationMode) var presentationMode self.presentationMode.wrappedValue.dismiss()
@Binding var showFavorited: Bool Toggle(isOn: $showFavorited) { Text("Change filter") }
fonte