Argh! Byłeś tak blisko. Tak to się robi. Pominięto znak dolara (beta 3) lub podkreślenie (beta 4) i albo self przed właściwością amount, albo .value za parametrem amount. Wszystkie te opcje działają:
Zobaczysz, że usunąłem @State
in includeDecimal, sprawdź wyjaśnienie na końcu.
To jest użycie własności (wstaw self):
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
}
}
lub używając .value after (ale bez self, ponieważ używasz przekazanego parametru, a nie właściwości struktury):
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
}
}
To jest to samo, ale używamy różnych nazw dla parametru (withAmount) i właściwości (amount), więc wyraźnie widzisz, kiedy używasz każdego z nich.
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
}
}
Należy zauważyć, że wartość .value nie jest konieczna w przypadku właściwości, dzięki opakowaniu właściwości (@Binding), które tworzy metody dostępu, które sprawiają, że wartość .value jest niepotrzebna. Jednak z parametrem czegoś takiego nie ma i trzeba to zrobić wprost. Jeśli chcesz dowiedzieć się więcej o opakowaniach właściwości, zajrzyj do sesji WWDC 415 - Modern Swift API Design i przejdź do 23:12.
Jak odkryłeś, modyfikacja zmiennej @State w inicjatorze spowoduje zgłoszenie następującego błędu: Wątek 1: Błąd krytyczny: Dostęp do State poza View.body . Aby tego uniknąć, należy usunąć @State. Ma to sens, ponieważ includeDecimal nie jest źródłem prawdy. Jego wartość wynika z kwoty. Usunięcie @State includeDecimal
nie spowoduje jednak aktualizacji w przypadku zmiany kwoty. Aby to osiągnąć, najlepszą opcją jest zdefiniowanie parametru includeDecimal jako obliczonej właściwości, tak aby jego wartość pochodziła ze źródła prawdy (kwoty). W ten sposób za każdym razem, gdy zmienia się kwota, zmienia się również parametr includeDecimal. Jeśli Twój widok zależy od includeDecimal, powinien zostać zaktualizowany po zmianie:
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 { ... }
}
Jak wskazał rob mayoff , możesz również użyć $$varName
(beta 3) lub _varName
(beta4), aby zainicjować zmienną stanu:
$$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
odThread 1: Fatal error: Accessing State<Bool> outside View.body
@State
zmienne powinny stanowić źródło prawdy. Ale w twoim przypadku powielasz tę prawdę, ponieważ wartość includeDecimal można wyprowadzić z twojego rzeczywistego źródła prawdy, którym jest kwota. Masz dwie możliwości: 1. Uczyniasz zmienną includeDecimal prywatną zmienną (bez @State) lub jeszcze lepiejamount
. W ten sposób również zmienia się kwotaincludeDecimal
. Powinieneś to zadeklarować w ten sposób:private var includeDecimal: Bool { return round(amount)-amount > 0 }
i usunąćself.includeDecimal = ...
includeDecimal
więc potrzebuję tego jako zmiennej @State w widoku. Naprawdę chcę zainicjować go wartością początkową.value
został zastąpiony przez.wrappedValue
, byłoby miło zaktualizować odpowiedź i usunąć opcje beta.Powiedziałeś (w komentarzu) „Muszę być w stanie się zmienić
includeDecimal
”. Co to znaczy zmienićincludeDecimal
? Najwyraźniej chcesz go zainicjować na podstawie tego, czyamount
(w czasie inicjalizacji) jest liczbą całkowitą. W porządku. Więc co się stanie, jeśliincludeDecimal
tak,false
a później zmienisz to natrue
? Czy zamierzasz w jakiś sposób wymusićamount
bycie niecałkowitym?W każdym razie nie możesz modyfikować
includeDecimal
winit
. Ale możesz go zainicjować winit
następujący sposób: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
(Należy pamiętać, że w pewnym momencie
$$includeDecimal
składnia zostanie zmieniony_includeDecimal
).źródło
Skoro jest połowa 2020 roku, podsumujmy:
Co się tyczy
@Binding amount
_amount
zaleca się używać tylko podczas inicjalizacji. I nigdy nie przypisuj w ten sposóbself.$amount = xxx
podczas inicjalizacjiamount.wrappedValue
iamount.projectedValue
nie są często używane, ale możesz zobaczyć przypadki takie jak@Environment(\.presentationMode) var presentationMode self.presentationMode.wrappedValue.dismiss()
@Binding var showFavorited: Bool Toggle(isOn: $showFavorited) { Text("Change filter") }
źródło