[SwiftUI] SwiftUIのForEach内でBinding変数を渡したい

SwiftUI

配列に保存されている変数を、Binding で渡したいときには、ひと工夫必要です。その方法を説明します。

SwiftUIを使っている時に、別のビュー内での変更を検知するためには、Bindingの変数を渡す必要があります。

Binding変数の渡し方

以下は、うまく動くケースです。ContentViewで定義されたvalue変数がBinding<Double>でSubView1に渡されています。
Structは、基本的には、値渡しですが、Bindingのために、参照渡しになり、SubView1の中での変更が、ContentViewにも反映されます。

BindingGood

コードはこちら

Binding 変数 例1

配列に保存されている変数をBindingで渡したい

変数をそのまま持っていれば上記のようにBindingで渡すことは、$をつけるだけですぐ出来ます。
ただ、配列で保持していて、その中の1つだけを渡したい時に、以下のようなコードを書くとエラーとなります。

example code

エラーは、”Cannot convert value of type ‘[Double]’ to expected argument type ‘Range<Int>'”と”Cannot convert value of type ‘Int’ to expected argument type ‘Binding<Double>’”

1つ目のエラーは、ForEachからのエラーで、渡された[Doulble]の配列では、それぞれの識別方法がわからないというエラーです。今回は、重ならない値を設定しているので、id:\.selfを設定することで、解決できるはずです。

2つ目のエラーが問題です。@Stateが付いているのは、[Double]であって、Doubleではないので、$をつけてBinding<Double>にすることができず、困ります。

values[n]という書き方はあり
ちなみに、上記でoneValueのところを、(意味がおかしいことは置いておいて) “self.$values[0]”と書くと、エラーが消えることが確認できます。言い換えますと、$values[index]と書くことで、Binding<Double>を渡すことは出来そうです。

ForEachでindexを使ってIterateしても動きません

では、ForEachを普通にIndexを使ってIterateすれば良いんじゃないの?となるかと思うのですが、以下のコードでは、コンパイルできるのですが、期待通りに動きません
スナップショットを見てもらうと、最下段のスライダーと40という数値が一致していないことを確認してもらえるかと思います。

BindingDoesNotWork

Index アクセス例

indicesを使う必要があります

以下のように配列のインデックスを取得するindicesを使うことでうまくBindingを渡すことができます。

indices 使用例

BindingWorksWithIndices

注意
このあたりは、色々と変更のあった箇所らしく、StackOverflowでも議論されてます。

仕様に変更が入るかもしれません。

まとめ:ForEach内でBindingを渡したい時

indicesを使って、ForEachを回すとBindingが渡せます。

注意
XCode11.4を使って、上記の動作を確認しています。将来的には、動作が変わっているかもしれませんので注意してください。

ワーニングが表示される時

使っている箇所によっては、実行時に、Idを指定しなさいというようなメッセージがDebugコンソールに表示されます。

そのような時は、(メッセージ通りですが)ForEach(values.indices, id:\.self)としてください。ワーニングが表示されなくなります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です