Pickerは複数の選択肢の中から1つの値をユーザーに選択させる為の部品です。
図のような回転ホイール表示が代表的なスタイルですが、プラットフォームによってスタイルが自動的に変わる特徴があります。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.1
【iOS】13.2.2
【macOS】Catalina バージョン 10.15.2
基本的な使い方
1 2 3 4 5 6 7 |
Picker(selection: $selection, label: Text("ラベル")) { Text("選択肢1").tag(1) Text("選択肢2").tag(2) ︙ } |
最初の引数(selection)には選択値と連携するプロパティを指定します。
このプロパティの値は初期選択値として使用され、選択が変更されると値も同じく変更されます。
値が変更できるように、このプロパティは@State等で宣言しておく必要があります。
頭に$が付いているのは、プロパティの値では無く参照を渡し、Picker()がプロパティの値を更新可能にする意味があります。
@Stateについては、こちらの記事を参照して下さい。

2番めの引数(label)は、Pickerに表示するラベルを指定します。View形式で指定できるので、単純なテキストだけでなく、シンボルやイメージなどを組み合わせ可能です。
最後に選択項目となるViewを列挙したクロージャーを記述します。
各選択項目に付加する.tag()は対象のViewを特定する目印です。この値がselectionに連携します。
tagの型はHashableプロトコルに準拠していれば良いので、数値、文字列等のSwift標準の型がほぼ使えますが、selectionで指定するプロパティと型を合わせる必要があります。
使用例
5種類のフルーツから1つを選択するPickerのサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct ContentView: View { @State private var selection = 3 // 選択値と連携するプロパティ var body: some View { VStack { Picker(selection: $selection, label: Text("フルーツを選択")) { Text("みかん").tag(1) Text("ぶどう").tag(2) Text("りんご").tag(3) Text("バナナ").tag(4) Text("もも").tag(5) } .frame(width: 400) Text("選択値:\(selection)") } } } |
選択値と連携するプロパティ(selection)の初期値が、初期選択値として使用されます。
Pickerの選択を変更するたびに、selectionが更新されるので、画面下の選択値の値が再描画されます。
選択肢を繰り返し処理(ForEach)で設定する
ForEachを使った繰り返し処理で選択肢のリストを設定できます。
これを使って配列等のデータコレクションを選択肢リストに設定可能です。
ForEachを使った場合、.tag()による値の設定は不要となります。
ForEachには、【範囲指定】と【id指定】の2種類の繰り返しがあります。
詳しくは、こちらの記事で解説しています。

範囲指定による繰り返し
ForEachの【範囲指定】による繰り返しで、選択肢を設定するサンプルソースです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
struct ContentView: View { let fruits = ["みかん", "ぶどう", "りんご", "バナナ", "もも"] @State private var selection = 3 // ここで設定した値(3)が初期選択値になる var body: some View { VStack { Picker(selection: $selection, label: Text("フルーツを選択")) { ForEach(0 ..< fruits.count) { num in Text(self.fruits[num]) // .tag()の指定は不要 } } .frame(width: 400) Text("選択値:\(selection)") } } } |
配列(fruits)はフルーツ名が格納されており、ForEachによる繰り返し処理で、各要素を選択肢として設定します。
ForEachで生成されたデータ列のインデックス(0から始まる)が自動的にselectionと連携するので、.tag()による値の設定は不要です。
selectionの初期値として3が設定されているため、データ列4番目の=“バナナ”が初期選択値として表示されます。
id指定による繰り返し
ForEachの【id指定】による繰り返しで、選択肢を設定するサンプルソースです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
struct ContentView: View { let fruits = ["みかん", "ぶどう", "りんご", "バナナ", "もも"] @State private var selection = "りんご" // ここで設定した値(りんご)が初期選択値になる var body: some View { VStack { Picker(selection: $selection, label: Text("フルーツを選択")) { ForEach(fruits, id: \.self) { fruit in // id指定の繰り返し Text(fruit) // .tag()の指定は不要 } } .frame(width: 400) Text("選択値:\(selection)") } } } |
【id指定】による繰り返しの場合は、id自身がselectionと連携します。
この例ではidは配列fruitsの各要素ですので、連携するselectionも同じ型(String)で作成します。
なお、idに指定される値は基本的にユニークであるのが前提となります。
配列に内容の要素があった場合、想定外の動きになりますので注意してください。
勘違いしやすいselection連携の落とし穴
小学校の学年を選択させる為の次のようなコードがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct ContentView: View { @State private var schoolYear = 3 var body: some View { VStack { Picker(selection: $schoolYear, label: Text("学年")) { ForEach(1 ..< 7) { num in Text("\(num)年生") } } Text("選択値:\(schoolYear)") } } } |
選択肢には1から6までの学年が表示され、初期値は3年生を設定しているつもりですが、想定通りに動きません。
初期値として4年生が選択されてしまいますし、1年生を選択すると、schoolYearには想定外の0が代入されます。
これは、【範囲指定】の繰り返しの場合、selectionはあくまでもデータ列のインデックス(0から始まる並び順)と連携するためです。
次にように【id指定】に変えて、ForEachのループアイテム値そのものと連携させると、想定した動きになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct ContentView: View { @State private var schoolYear = 3 var body: some View { VStack { Picker(selection: $schoolYear, label: Text("学年")) { ForEach(1 ..< 7, id: \.self) { num in // id指定による繰り返し Text("\(num)年生") } } Text("選択値:\(schoolYear)") } } } |
表示スタイルの指定
Pickerは単独で使用した場合は回転ホイールスタイルですが、フォームで使用すると、図のように別画面に遷移して選択するスタイルに自動的に変わります。
内部的にNavigationLinkを使って遷移する為、NavigationViewの中に記述する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct ContentView: View { let fruits = ["みかん", "ぶどう", "りんご", "バナナ", "もも"] @State private var selection = 3 var body: some View { NavigationView { // Formで使う場合はNavigationViewが必須 Form { Picker(selection: $selection, label: Text("フルーツを選択")) { ForEach(0 ..< fruits.count) { num in Text(self.fruits[num]) } } Text("選択値:\(selection)") } } } } |
スタイルの任意指定
Modifierの.pickerStyle()を使用すると、Pickerの表示スタイルを任意に指定できます。
iOSでは現時点で指定できるのは次の2種類のみです。
WheelPickerStyle()
1 2 3 |
.pickerStyle(WheelPickerStyle()) |
標準的な回転ホイールスタイルです。
Form内で指定すると、自動的に採用される画面遷移型の代わりに回転ホイール型で表示されます。
SegmentedPickerStyle()
1 2 3 |
.pickerStyle(SegmentedPickerStyle()) |
水平リストに選択肢を並べる表示タイプで選択肢が少ない場合に最適です。
なお、このスタイルの場合ラベルは表示されません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct ContentView: View { let fruits = ["みかん", "ぶどう", "りんご", "バナナ", "もも"] @State private var selection = 3 var body: some View { VStack { Picker(selection: $selection, label: Text("フルーツを選択")) { ForEach(0 ..< fruits.count) { num in Text(self.fruits[num]) } } .pickerStyle(SegmentedPickerStyle()) // セグメントピッカースタイルの指定 .frame(width: 400) Text("選択値:\(selection)") } } } |
ラベルを自由に配置する
Pickerの標準ラベルはレイアウトの柔軟性に乏しいので、自由にラベルを配置したい場合は、.labelsHidden()を使って標準ラベルを非表示にすると良いです。
非表示になった標準ラベルですが、VoiceOverによるスクリーンリーダーでは有効ですので、適切な文言をいれて残しておきましょう。
こちらは、ラベルを回転ホイールの上部に表示させるサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct ContentView: View { let fruits = ["みかん", "ぶどう", "りんご", "バナナ", "もも"] @State private var selection = 3 var body: some View { VStack { Text("フルーツを選択").font(.title) // 標準ラベルの代わり Picker(selection: $selection, label: Text("フルーツを選択")) { ForEach(0 ..< fruits.count) { num in Text(self.fruits[num]) } } .labelsHidden() // 標準ラベルを非表示にする。 Text("選択値:\(selection)") } } } |
あわせて読みたい記事
