Viewの上にこれらのコントロール(UI部品)を配置してアプリを作成していきます. 今回は基本部品の一つPickerです.
環境は,
- macOS Catalina 10.15.7
- Xcode 12.0.1
- Swift 5.3
- iOS 14.0
です.
目次
Pickerとは
複数の選択肢の中から1つを選ぶことができるViewです.

Pickerの基本的なinitializer
init(selection: Binding<SelectionValue>, label: Label, content: () -> Content)
- selection: Pickerで選んだ値をこの状態変数で受け取ることができます
- label: Pickerのラベルに表示されるView
- content:選択項目をここに書きます
Pickerの具体例
簡単な例
import SwiftUI
struct ContentView: View {
@State var selectedText = "赤"
var body: some View {
VStack{
Picker(
selection: $selectedText,
label: Text("色を選んでください")
){
Text("赤").tag("赤")
Text("青").tag("青")
Text("黄").tag("黄")
Text("緑").tag("緑")
Text("紫").tag("紫")
}
Text(selectedText)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

現在のmacOS Catalina 10.15.7, Xcode 12.0.1, Swift 5.3, iOS 14.0という環境では, labelが表示されません. コードの書き方が間違っているんでしょうか…

PickerStyleを変える
Pickerにはいくつかのスタイルが用意されています.
スタイル | 説明 |
---|---|
DefaultPickerStyle | デフォルト |
SegmentedPickerStyle | セグメント型のPicker |
WheelPickerStyle | ホイール型のPicker |
ただ, DefaultPickerStyleはWheelPickerStyleなので, 他のタイプはSegmentedPickerStyleになります.
次のようなものがSegmentedPickerStyleです.
import SwiftUI
struct ContentView: View {
@State var selectedText = "赤"
var body: some View {
VStack{
Picker(
selection: $selectedText,
label: Text("色を選んでください")
){
Text("赤").tag("赤")
Text("青").tag("青")
Text("黄").tag("黄")
Text("緑").tag("緑")
Text("紫").tag("紫")
}
.pickerStyle(SegmentedPickerStyle())
.padding()
Text(selectedText)
}
}
}

Pickerの選択項目に列挙型を用いる
このコードは公式サイトのものからの引用です.
import SwiftUI
enum Flavor: String, CaseIterable, Identifiable {
case chocolate
case vanilla
case strawberry
var id: String { self.rawValue }
}
struct ContentView: View {
@State private var selectedFlavor = Flavor.chocolate
var body: some View {
Picker("Flavor", selection: $selectedFlavor) {
Text("Chocolate").tag(Flavor.chocolate)
Text("Vanilla").tag(Flavor.vanilla)
Text("Strawberry").tag(Flavor.strawberry)
}
Text("Selected flavor: \(selectedFlavor.rawValue)")
}
}

選択肢にForEachを用いる
選択肢をForEachで作成することもできます. 他のものと似ているので画像は省略します.
import SwiftUI
struct ContentView: View {
var colors = ["赤", "青", "黄", "緑", "紫"]
@State var selected = 0
var body: some View {
VStack{
Picker(
selection: $selected,
label: Text("色を選んでください")
){
ForEach(0..<colors.count){
Text(self.colors[$0])
}
}
.padding()
Text("選んだ色は\(colors[selected])です")
}
}
}
PickerをHStackを用いて横に2つ並べる
はまりました. 普通にコードを書くとうまくいきませんでした. 例えば, 次のように並べると, うまく真ん中にいきません.
import SwiftUI
struct ContentView: View {
var column = ["1", "2", "3", "4", "5"]
var row = ["1", "2", "3", "4", "5"]
@State var selectedColumn = 0
@State var selectedRow = 0
var body: some View {
VStack{
HStack{
Picker(
selection: $selectedColumn,
label: Text("Selected cloumn")
){
ForEach(0..<column.count){
Text(self.column[$0])
}
}
.border(Color.red)
Picker(
selection: $selectedRow,
label: Text("Selected row")
){
ForEach(0..<row.count){
Text(self.row[$0])
}
}
.border(Color.blue)
}
}
}
}

こんなところで悩むと思わず, 呆然. 悩んでいたところ, 解決策がありました. Stack Overflowにはお世話になりっぱなしです.
この回答を参考にコードを書くと次のようになります.
import SwiftUI
struct ContentView: View {
var column = ["1", "2", "3", "4", "5", "6", "7"]
var row = ["1", "2", "3", "4", "5", "6", "7"]
@State var selectedColumn = 0
@State var selectedRow = 0
var body: some View {
GeometryReader { geometry in
VStack{
Text("座席数を選んでください")
.padding()
.foregroundColor(.pink)
HStack(spacing: 0) {
VStack {
Text("何行目")
Picker(
selection: $selectedColumn,
label: Text("Selected cloumn")
){
ForEach(0..<column.count){
Text(self.column[$0])
}
}
.frame(maxWidth: geometry.size.width / 2)
.clipped()
}
VStack {
Text("何列目")
Picker(
selection: $selectedRow,
label: Text("Selected row")
){
ForEach(0..<row.count){
Text(self.row[$0])
}
}
.frame(maxWidth: geometry.size.width / 2)
.clipped()
}
}
HStack{
Text("選んだ座席数は")
Text("\(selectedColumn + 1) × \(selectedRow + 1)")
.font(.title)
.foregroundColor(.purple)
Text("です")
}
}
}
}
}

pickerの値に応じて座席のButtonを表示させる
思ったより大変でした. そしてコードが見にくくてすみません.
import SwiftUI
struct ContentView: View {
var column = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
var row = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
@State var selectedColumn = 0
@State var selectedRow = 0
var body: some View {
GeometryReader { geometry in
VStack{
Text("座席数を選んでください")
.padding()
.foregroundColor(.pink)
HStack(spacing: 0) {
VStack {
Text("何行目")
Picker(
selection: $selectedColumn,
label: Text("Selected cloumn")
){
ForEach(0..<column.count){
Text(self.column[$0])
}
}
.frame(maxWidth: geometry.size.width / 2)
.clipped()
}
VStack {
Text("何列目")
Picker(
selection: $selectedRow,
label: Text("Selected row")
){
ForEach(0..<row.count){
Text(self.row[$0])
}
}
.frame(maxWidth: geometry.size.width / 2)
.clipped()
}
}
HStack{
Text("選んだ座席数は")
Text("\(selectedColumn + 1) × \(selectedRow + 1)")
.font(.title)
.foregroundColor(.purple)
Text("です")
}
SubView(row: $selectedRow, column: $selectedColumn)
.padding()
}
}
}
}
struct SubView: View {
@Binding var row: Int
@Binding var column: Int
var seatsNumber = 1
var body: some View {
VStack{
ForEach(0..<column+1, id:\.self) {_ in
HStack{
ForEach(0..<row+1, id:\.self) {_ in
Button(action: {}) {
Text("").modifier(CustomModifier())
}
}
}
}
}
}
}
//ボタン内のテキストの装飾
struct CustomModifier: ViewModifier {
func body(content: Content) -> some View {
content
.frame(width: 20)
.padding(5)
.foregroundColor(.red)
.background(Color.blue)
.cornerRadius(5)
.overlay(RoundedRectangle(cornerRadius: 5)
.stroke(Color.pink, lineWidth: 1)
)
}
}

コメント