やりたいこと
フォームなどで Enum の値を Picker で選択させたい。
このとき、未選択の状態を nil で表現し、nil の場合は「選択してください」という項目にしたい。
実装
上の画像の実装コードを載せる。
enum Area: String, CaseIterable {
case africa = "アフリカ"
case asia = "アジア"
case europa = "ヨーロッパ"
case america = "アメリカ"
case australia = "オーストラリア"
}
struct FormView: View {
@State private var area: Area? = nil
var body: some View {
VStack {
Text(area?.rawValue ?? "nilです")
Picker("エリア", selection: $area) {
Text("選択してください").tag(Area?(nil))
ForEach(Area.allCases, id: \.self) { area in
Text(area.rawValue).tag(Area?.some(area))
}
}
}
}
}
解説
enum
String
に準拠させ Picker に表示する文字を指定しておく。
さらに CaseIterable
で enum を配列化できるようにする。
enum Area: String, CaseIterable {
case africa = "アフリカ"
case asia = "アジア"
case europa = "ヨーロッパ"
case america = "アメリカ"
case australia = "オーストラリア"
}
Picker
重要なのは tag
の振る舞い。
Picker の selection に適用された値に tag の値が連動するはずだが、以下だと動かない。
(そもそもこれだと nil の時の振る舞いが書かれていないが...)
struct FormView: View {
@State private var area: Area? = nil
var body: some View {
VStack {
Text(area?.rawValue ?? "nilです")
Picker("エリア", selection: $area) {
ForEach(Area.allCases, id: \.self) { area in
Text(area.rawValue).tag(area)
}
}
}
}
}
これは tag に与えられた area
の型は Area型
だが、selection
に適応されているのは Area?型
であるという違いが原因。
というわけで、Area?.some(area)
でラップしてあげよう。
Picker("エリア", selection: $area) {
ForEach(Area.allCases, id: \.self) { area in
Text(area.rawValue).tag(Area?.some(area))
}
}
あとは nil の際の振る舞いを追加する。
こちらもタグを使い、Area?
の nil であることを表現してあげよう。
Picker("エリア", selection: $area) {
Text("選択してください").tag(Area?(nil))
ForEach(Area.allCases, id: \.self) { area in
Text(area.rawValue).tag(Area?.some(area))
}
}