SwiftUIでアプリを作る際、ユーザーに選択肢を提供する場面は頻繁に訪れます。そんな時に活躍するのがPickerコンポーネントです。
この記事では、SwiftUI初心者の方でも理解できるように、Pickerの基本から実践的な使い方、カスタマイズ方法まで、分かりやすく解説していきます。
Pickerとは?基本を理解しよう
Pickerは、複数の選択肢の中から1つを選んでもらうためのUIコンポーネントです。iOSアプリの「設定」画面などでよく見かける、あの選択UIを簡単に実装できます。
Pickerでできること
- ドロップダウンメニューの表示
- セグメントコントロール(タブ形式)の実装
- ホイール形式の選択UI(日付ピッカーのような回転式)
- ナビゲーションリンク形式の選択画面
これらすべてを、同じPickerコンポーネントで実現できます。
最もシンプルなPickerの実装
まずは、基本的なPickerを作ってみましょう。
import SwiftUI
struct ContentView: View {
@State private var selectedFruit = "りんご"
let fruits = ["りんご", "バナナ", "オレンジ", "ぶどう"]
var body: some View {
VStack(spacing: 20) {
Picker("好きな果物", selection: $selectedFruit) {
ForEach(fruits, id: \.self) { fruit in
Text(fruit)
}
}
Text("選択された果物: \(selectedFruit)")
.font(.headline)
}
.padding()
}
}
コードの解説
@State private var selectedFruit 現在選択されている値を保持する変数です。@State
を使うことで、値が変更されるとUIが自動的に更新されます。
selection: $selectedFruit $
マークは「バインディング」を意味します。Pickerで選択された値がselectedFruit
に自動的に反映されます。
ForEach(fruits, id: .self) 配列の各要素をPickerの選択肢として表示します。id: \.self
は各要素を一意に識別するために使います。
Pickerの4つの主要スタイル
Pickerは.pickerStyle()
修飾子を使って、表示形式を変更できます。
スタイル1: Segmented(セグメント)
タブのような見た目で、2〜5個程度の選択肢に最適です。
struct SegmentedPickerExample: View {
@State private var selectedTheme = "ライト"
var body: some View {
VStack {
Picker("テーマ選択", selection: $selectedTheme) {
Text("ライト").tag("ライト")
Text("ダーク").tag("ダーク")
Text("自動").tag("自動")
}
.pickerStyle(.segmented)
.padding()
Text("現在のテーマ: \(selectedTheme)")
}
}
}
使用シーン:
- テーマ切り替え
- 表示モードの選択(リスト/グリッド)
- フィルター選択
スタイル2: Wheel(ホイール)
くるくる回転するドラム式の選択UIです。
struct WheelPickerExample: View {
@State private var selectedAge = 20
let ages = Array(0...100)
var body: some View {
VStack {
Picker("年齢を選択", selection: $selectedAge) {
ForEach(ages, id: \.self) { age in
Text("\(age)歳").tag(age)
}
}
.pickerStyle(.wheel)
.frame(height: 150)
Text("選択された年齢: \(selectedAge)歳")
}
}
}
使用シーン:
- 年齢や数値の選択
- 時刻や日付の選択
- 多数の選択肢から1つを選ぶ場合
スタイル3: Menu(メニュー)
ドロップダウンメニュー形式で表示されます。iOS 14以降で使用可能です。
struct MenuPickerExample: View {
@State private var selectedCountry = "日本"
let countries = ["日本", "アメリカ", "イギリス", "フランス", "ドイツ"]
var body: some View {
VStack {
Picker("国を選択", selection: $selectedCountry) {
ForEach(countries, id: \.self) { country in
Text(country).tag(country)
}
}
.pickerStyle(.menu)
Text("選択された国: \(selectedCountry)")
}
.padding()
}
}
使用シーン:
- 国や地域の選択
- カテゴリー選択
- 多数の選択肢がある場合
スタイル4: Navigation Link(ナビゲーション)
別画面に遷移して選択する形式です。Form
やList
内で使用すると自動的にこのスタイルになります。
struct NavigationPickerExample: View {
@State private var selectedLanguage = "Swift"
let languages = ["Swift", "Kotlin", "Java", "Python", "JavaScript"]
var body: some View {
NavigationView {
Form {
Picker("プログラミング言語", selection: $selectedLanguage) {
ForEach(languages, id: \.self) { language in
Text(language).tag(language)
}
}
Section {
Text("選択: \(selectedLanguage)")
}
}
.navigationTitle("設定")
}
}
}
使用シーン:
- 設定画面
- 多数の選択肢がある場合
- 各選択肢に説明が必要な場合
実践的な使用例
例1: 設定画面の実装
struct SettingsView: View {
@State private var notificationEnabled = true
@State private var selectedLanguage = "日本語"
@State private var fontSize = "中"
@State private var theme = "システム設定"
let languages = ["日本語", "English", "中文", "한국어"]
let fontSizes = ["小", "中", "大", "特大"]
let themes = ["ライト", "ダーク", "システム設定"]
var body: some View {
NavigationView {
Form {
Section(header: Text("通知")) {
Toggle("通知を許可", isOn: $notificationEnabled)
}
Section(header: Text("表示設定")) {
Picker("言語", selection: $selectedLanguage) {
ForEach(languages, id: \.self) { language in
Text(language).tag(language)
}
}
Picker("フォントサイズ", selection: $fontSize) {
ForEach(fontSizes, id: \.self) { size in
Text(size).tag(size)
}
}
Picker("テーマ", selection: $theme) {
ForEach(themes, id: \.self) { theme in
Text(theme).tag(theme)
}
}
}
}
.navigationTitle("設定")
}
}
}
例2: Enumを使った型安全なPicker
より安全なコードを書くために、Enumを使用することをおすすめします。
enum Theme: String, CaseIterable, Identifiable {
case light = "ライト"
case dark = "ダーク"
case auto = "自動"
var id: String { self.rawValue }
}
struct ThemePickerView: View {
@State private var selectedTheme: Theme = .light
var body: some View {
VStack {
Picker("テーマ選択", selection: $selectedTheme) {
ForEach(Theme.allCases) { theme in
Text(theme.rawValue).tag(theme)
}
}
.pickerStyle(.segmented)
.padding()
Text("選択されたテーマ: \(selectedTheme.rawValue)")
.padding()
.background(backgroundColor)
.cornerRadius(10)
}
}
var backgroundColor: Color {
switch selectedTheme {
case .light:
return .white
case .dark:
return .black
case .auto:
return .gray
}
}
}
例3: 複数のPickerを連動させる
地域と都市を連動させる実装例です。
struct LocationPickerView: View {
@State private var selectedRegion = "関東"
@State private var selectedCity = "東京"
let regions = ["関東", "関西", "中部"]
let cities: [String: [String]] = [
"関東": ["東京", "神奈川", "千葉", "埼玉"],
"関西": ["大阪", "京都", "兵庫", "奈良"],
"中部": ["愛知", "静岡", "岐阜", "三重"]
]
var body: some View {
Form {
Picker("地域", selection: $selectedRegion) {
ForEach(regions, id: \.self) { region in
Text(region).tag(region)
}
}
.onChange(of: selectedRegion) { newRegion in
// 地域が変更されたら、その地域の最初の都市を選択
selectedCity = cities[newRegion]?.first ?? ""
}
Picker("都道府県", selection: $selectedCity) {
ForEach(cities[selectedRegion] ?? [], id: \.self) { city in
Text(city).tag(city)
}
}
Section {
Text("選択: \(selectedRegion) - \(selectedCity)")
}
}
}
}
Pickerのカスタマイズテクニック
ラベルの非表示
Picker(selection: $selection) {
// 選択肢
} label: {
EmptyView()
}
.labelsHidden()
色のカスタマイズ
Picker("選択", selection: $selection) {
ForEach(options, id: \.self) { option in
Text(option).tag(option)
}
}
.pickerStyle(.segmented)
.background(Color.blue.opacity(0.1))
.cornerRadius(8)
フォントのカスタマイズ
Picker("選択", selection: $selection) {
ForEach(options, id: \.self) { option in
Text(option)
.font(.headline)
.tag(option)
}
}
よくある間違いと解決方法
間違い1: tagを忘れる
// ❌ 間違い
Picker("選択", selection: $selectedValue) {
Text("オプション1") // tagがない
Text("オプション2")
}
// ✅ 正しい
Picker("選択", selection: $selectedValue) {
Text("オプション1").tag("オプション1")
Text("オプション2").tag("オプション2")
}
間違い2: バインディング($)を忘れる
// ❌ 間違い
Picker("選択", selection: selectedValue) { // $がない
// ...
}
// ✅ 正しい
Picker("選択", selection: $selectedValue) { // $を付ける
// ...
}
間違い3: @Stateを忘れる
// ❌ 間違い
var selectedValue = "" // @Stateがない
// ✅ 正しい
@State private var selectedValue = "" // @Stateを付ける
パフォーマンスの最適化
大量の選択肢を扱う場合は、以下の点に注意しましょう。
struct OptimizedPickerView: View {
@State private var selectedItem = 0
// 選択肢が多い場合は、配列として定義
let items = Array(0..<1000)
var body: some View {
Picker("選択", selection: $selectedItem) {
ForEach(items, id: \.self) { item in
Text("Item \(item)")
.tag(item)
}
}
.pickerStyle(.wheel)
}
}
大量のデータを扱う場合は、Wheelスタイルがパフォーマンス面で有利です。
まとめ
SwiftUIのPickerは、ユーザーに選択肢を提供する強力なコンポーネントです。
重要なポイント:
@State
と$
(バインディング)を使って値を管理.pickerStyle()
で見た目を変更できる- Segmented: 少数の選択肢向け
- Wheel: 数値や日付の選択向け
- Menu: 多数の選択肢向け
- Navigation: 設定画面向け
- Enumを使うと型安全なコードが書ける
.tag()
を忘れずに付ける
Pickerは非常に汎用性が高いコンポーネントなので、様々な場面で活用できます。まずは簡単な例から始めて、徐々に複雑な実装にチャレンジしてみてください。
実際にコードを書いて動かしてみることが、理解への一番の近道です。Xcodeのプレビュー機能を使って、色々なスタイルを試してみましょう!