質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2448閲覧

【SwiftUI】PickerでForEachを使用し一意なidを付与してもselectionの値が変動しないのはなぜ?

bbdd

総合スコア43

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2021/06/19 02:47

前提

Xcode 12.5
下部にあります「検証用コード」を実行することで本件の事象を再現できます。

疑問点

SwiftUIにてPickerで次のようなデータ構造を持った配列を表示するとします。

swift

1struct DemoPickItem: Identifiable, Hashable { 2 var id: String { databaseID } 3 var databaseID: String 4 var name: String 5} 6

コンテンツを表示する際に、次のようにForEachの引数idDemoPickItemiddatabaseIDを設定すると、Pickerselectionに設定した値が変更されません。

swift

1 Picker("", selection: $demo) { 2 ForEach(datas, id: .id) { data in 3 Text("(data.name) : (data.databaseID)") 4 } 5 }

一方で、selfを設定するとselectionに設定した値がPickerでの選択状況に応じて変動します。

自分の認識ですと、ForEachidに設定している値がDemoPickItemiddatabaseIDである場合、それは一意であり静的なので「selectionに設定した値がPickerでの選択状況に応じて変動」すると思っていました。
しかし、上述の通りそうならなかったのですがその理由がわからないです。
アドバイス頂けますと幸いです。

検証用コード

swift

1 2// MARK: Entity 3 4struct DemoPickItem: Identifiable, Hashable { 5 var id: String { databaseID } 6 var databaseID: String 7 var name: String 8} 9 10let demoPickItems = [ 11 DemoPickItem(databaseID: "01", name: "demo-name1"), 12 DemoPickItem(databaseID: "02",name: "demo-name2"), 13 DemoPickItem(databaseID: "03",name: "demo-name3"), 14 DemoPickItem(databaseID: "04",name: "demo-name4"), 15 DemoPickItem(databaseID: "05",name: "demo-name5"), 16] 17 18// MARK: View 19 20import SwiftUI 21 22struct DemoPickerDialogView01: View { 23 let datas: [DemoPickItem] 24 @State var demo: DemoPickItem = DemoPickItem(databaseID: "00",name: "demo-name") 25 26 var body: some View { 27 VStack { 28 Text(demo.name) 29 .padding(.top, 20) 30 31 Picker("", selection: $demo) { 32 ForEach(datas, id: .id) { data in 33 Text("(data.name) : (data.databaseID)") 34 } 35 } 36 .buttonStyle(PlainButtonStyle()) 37 38 Button(action: { 39 }) { 40 Text("閉じる") 41 .frame(minWidth: 0, maxWidth: .infinity) 42 .padding(.bottom, 20) 43 } 44 45 } 46 .cornerRadius(8) 47 } 48} 49 50#if DEBUG 51 52struct DemoPickerDialogView_Previews: PreviewProvider { 53 struct ContentView01: View { 54 var datas = demoPickItems 55 @State var data: DemoPickItem = demoPickItems[0] 56 @State private var dialogPresentation = DialogPresentation() 57 58 var body: some View { 59 NavigationView { 60 VStack { 61 DemoPickerDialogView01( 62 datas: demoPickItems 63 ) 64 } 65 } 66 } 67 } 68 69 static var previews: some View { 70 71 Group { 72 ContentView01() 73 } 74 } 75} 76 77#endif 78 79

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Pickerのselectionで指定した変数に入る値は、ForEachで指定した配列の要素が入るのではなく、ForEachのidで指定した変数の値が入ります。idに .selfと指定すれば、配列の要素がidに指定されたことになり、selectionには配列の要素が入ります。現在は、ForEachのidに.idを指定しているので、String型の値がselectionに入ることになりますが、selectionに指定されている変数($demo)がDemoPickItem型なので、設定されなかったのだと思います。(このような場合、どこかでエラーを検出すべきように思いますが、SwiftUIはまだまだ発展途上でうまくエラー検出できないのかなと思います。)

ForEachのidに.idを指定した場合は、

swift

1@State var selectedID = ""

のようなString型の@State変数を宣言し、それを selection: $selectedID のようにPickerに指定すれば、Pickerの選択状況がselectedIDに格納されるようになります。なお、DemoPickItemIdentifiableに準拠させていますので、この場合 id: .id を省略して、 ForEach(datas) { data inとだけ書いても良いと思います。

ただ、今回Pickerで取得したいのは選択された要素のidではなく、選択された要素を取得したいのだろうと思うので、ForEachのidに.selfを指定して、Pickerのselectionに $demo を指定するのが正しいと思います。

投稿2021/06/19 14:57

TakeOne

総合スコア6299

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問