Pickerのselectionの初期値をnilにしたい
小ネタです。Pickerで扱う値の初期値をnilにしたい場合にどうすれば扱えるか考えました
やりたいこと
いくつかの要素の中から一つを選択させたい場合 Picker の使用が候補に上がります。Picker(_:selection:content)やその他のinitializerに必要な selection部分の型は Binding<SelectValue> となっており SelectValue はHashableである必要があります。Pickerの content 部分はよくある書き方として ForEach が使われると思います。以下のようなコードです
enum Kind: String, CaseIterable {
case one, two, three
}
@State var kind: Kind
Picker("", selection: $kind) {
ForEach(Kind.allCases, id: \.self) { kind in
Text(kind.rawValue).tag(kind)
}
}
よく見るコードだと思います。one,two,threeのいずれかを選ばせたい場合のコードになります。初期値がone,two,threeのいずれかで済む場合はこれで良いのですが、困る場合は初期値は無しでユーザーに何かしら選ばせたい場合になると思います。とりあえず case none みたいなものを追加する案がすぐに思い浮かぶと思います。以下のような具合です

コード
import SwiftUI
enum Kind: String, CaseIterable {
case none
case one, two, three
var text: String {
switch self {
case .none:
return "Please Select"
case _:
return "\(self)"
}
}
}
struct ContentView: View {
@State var kind: Kind = .none
var body: some View {
Picker("", selection: $kind) {
ForEach(Kind.allCases, id: \.self) { kind in
Text(kind.text).tag(kind)
}
}
Button {
print("Run")
} label: {
Text("Run")
}
.disabled(kind == .none)
}
}
#Preview {
ContentView()
}
ここで @State var kind: Kind? = nil で宣言できないかなあ。と思って考えた内容を次に書きます
解決策
.tag に {N} as Kind? と書くことで行けます


コード
import SwiftUI
enum Kind: String, CaseIterable {
case one, two, three
}
struct ContentView: View {
@State var kind: Kind? = nil
var body: some View {
Picker("", selection: $kind) {
Text("Please select").tag(nil as Kind?)
ForEach(Kind.allCases, id: \.self) { kind in
Text(kind.rawValue).tag(kind as Kind?)
}
}
Button {
print("Run")
} label: {
Text("Run")
}
.disabled(kind == nil)
}
}
#Preview {
ContentView()
}
とりあえずできました。ちなみに .tag(nil) はコンパイルエラーになります。そして、Optional<Wrapped>のWrappedがHashableならOptionalもHashableになります。なのでこの書き方でもコンパイルが通ります。こちらの書き方の利点はnilを扱えることで、よく無い点は kind as Kind? を書かずに .tag(kind) と書くと動かなくなる点です。
まとめ
ちょっとした発見だったので記事にしました。
どちらが良いか。っていうのは置いておいて、「そうかこれでいいんだな」って発見をしたので記事にしました。ちなみに私は Optional<Kind> にする時の kind as Kind? が勢いで消されそうなので採用を避ける or コメントを残して採用とかでしょう。ちなみに case noneの例の部分の none というワードも避けたい気持ちがあるので別の名前とかにしそうです。
まさかの記事の内容をすべて否定する結論を書いてしまいました。ですが、この考え方は他の場所でも活かす部分がありそうなのでこれからもこういう書き方ができたのか。っていう発見をして面白いのがあったら記事にします
おしまい \(^o^)/
Discussion