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


102

私はお金の入力画面で作業して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
    }
    ...
}

回答:


163

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

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

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

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を使用します(ただし、構造体のプロパティではなく、渡されたパラメーターを使用しているため、selfは使用しません)。

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(beta 3)または_varName(beta4)を使用して状態変数を初期化することもできます。

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

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

ありがとう!これは大いに役立ちました!私は上のランタイムエラーを取得していますself.includeDecimal = round(self.amount)-self.amount > 0Thread 1: Fatal error: Accessing State<Bool> outside View.body
keegan3d

まあ、それはちょっと理にかなっています。@State変数は信頼できる情報源を表す必要があります。しかし、あなたの場合、includeDecimalの値は、実際の信頼できる情報源であるamountから導出できるため、その真実を複製しています。2つのオプションがあります:1。includeDecimalをプライベート変数(@Stateなし)にするか、さらに良いものにします。2 amount。から値を派生させる計算プロパティにします。このように、量が変わると、それincludeDecimalも変わります。次のように宣言する必要があります。private var includeDecimal: Bool { return round(amount)-amount > 0 }self.includeDecimal = ...
kontiki19年

うーん、変更includeDecimalできる必要があるので、ビューの@State変数として必要です。私は本当にただの開始値で初期化したい
keegan3d

1
@ Let's_Create私は完全に一度だけ、それらを見て、しかしに感謝の神転送のボタン;-)
コンティキ

1
本当にいい説明、ありがとう。私は今、.valueがに置き換えられたと思います.wrappedValue、答えを更新してベータオプションを削除するといいでしょう。
user10460 3719

11

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

とにかく、あなたが変更することはできませんincludeDecimalの中でinit。ただし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。)


ああすごい、ダブル$$は私がこの部分に必要なものでした!
keegan3d

3

2020年半ばなので、要約してみましょう。

について @Binding amount

  1. _amount初期化中にのみ使用することをお勧めします。またself.$amount = xxx、初期化中にこのように割り当てないでください

  2. amount.wrappedValueそしてamount.projectedValue頻繁に使用されていませんが、次のような例を見ることができます

@Environment(\.presentationMode) var presentationMode

self.presentationMode.wrappedValue.dismiss()
  1. @bindingの一般的な使用例は次のとおりです。
@Binding var showFavorited: Bool

Toggle(isOn: $showFavorited) {
    Text("Change filter")
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.