web-dev-qa-db-ja.com

SwiftUI:@Binding変数を使用してカスタム初期化を実装する方法

私はお金の入力画面で作業しており、初期化された金額に基づいて状態変数を設定するためにカスタムinitを実装する必要があります。

これはうまくいくと思っていましたが、次のコンパイラエラーが発生します。

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
    }
    ...
}
46
keegan3d

ああ!あなたはとても近かった。これがあなたのやり方です。ドル記号(ベータ3)またはアンダースコア(ベータ4)を逃し、amountプロパティの前にselfを入力したか、amountパラメータの後に.valueを入力しなかった。これらのオプションはすべて機能します。

@Statein includeDecimalを削除したことがわかります。最後の説明を確認してください。

これはプロパティを使用しています(自分の前に自己を置きます):

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
    }
}

または、後に.valueを使用します(ただし、構造体のプロパティではなく、渡されたパラメーターを使用しているため、自己なし)。

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
    }
}

これは同じですが、パラメータ(withAmount)とプロパティ(amount)に異なる名前を使用しているため、それぞれをいつ使用するかが明確にわかります。

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
    }
}

プロパティラッパー(@Binding)のおかげで、プロパティに.valueは不要です。これにより、.valueが不要になるアクセサーが作成されます。ただし、パラメーターではそのようなことはなく、明示的に行う必要があります。プロパティラッパーの詳細については、 WWDCセッション415-Modern Swift API Design を確認して23:12にジャンプしてください。

発見したように、初期化子から@State変数を変更すると、次のエラーがスローされます。スレッド1:致命的なエラー:View.body。これを回避するには、@ Stateを削除する必要があります。 includeDecimalは真実のソースではないため、これは理にかなっています。その値は金額から導出されます。ただし、@ Stateを削除すると、量が変わってもincludeDecimalは更新されません。これを実現するための最良のオプションは、includeDecimalを計算されたプロパティとして定義し、その値が真のソース(量)から派生するようにすることです。このように、量が変化するたびに、includeDecimalも変化します。ビューがincludeDecimalに依存している場合は、変更時に更新する必要があります。

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 { ... }
}

rob mayoffで示されているように、$$varName(ベータ3)、または_varName(beta4)でState変数を初期化します。

// Beta 3:
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)

// Beta 4:
_includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
63
kontiki

あなたは(コメントで)「私はincludeDecimalを変更できるようにする必要があります」と言った。 includeDecimalを変更するとはどういう意味ですか? amount(初期化時)が整数かどうかに基づいて初期化したいようです。はい。では、includeDecimalfalseで、後でtrueに変更するとどうなりますか?どうにかしてamountを非整数に強制しますか?

とにかく、includeDecimalinitを変更することはできません。ただし、次のようにinitで初期化できます。

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

ある時点で$$includeDecimal構文は_includeDecimalに変更されることに注意してください。)

4
rob mayoff