#25 UIPickerView|日本地名選擇

Chia
彼得潘的 Swift iOS / Flutter App 開發教室
10 min readMar 19, 2022

練習用pickerView實作一個可以選擇日本地名的pickerView,將使用者選擇的地名印在下方label中。

日本地名選擇的pickerView

首先先介紹最基本的pickerView用法,最後介紹如何完成上面的pickerView,文章分成下列三部分介紹。
-單個component的pickerView
-多個component的pickerView
-實作日本地名選擇的pickerView

單個component的pickerView

首先拉一個PickerView到Storyboard,拉完之後看Storyboard會長這樣。

Storyboard的PickerView

但這時候如果執行模擬器會看到下面這樣,一片空白的PickerView。

模擬器的PickerView

這是因為還沒有設定PickerView的程式碼。設定程式碼前先拉pickerView的IBOutlet。

@IBOutlet var pickerView: UIPickerView!

在viewDidLoad()設定pickerView的delegate及dataSource。

pickerView.delegate = self
pickerView.dataSource = self

用extension定義UIPickerViewDelegate及UIPickerViewDataSource

extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 10
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "\(row)"
}
}

numberOfComponents:指定有幾排pickerView選單,這邊設定為1代表只有一排
numberOfRowsInComponent:指定每一個component有幾個選項(row),這邊回傳10,所以會有十個選項可以選
titleForRow, forComponet:指定每一個選項顯示的標題,這邊設定為顯示row的編號

完成後的pickerView如下:

完成的pickerView

當然我們設計pickerView最主要目的就是要知道使用者選擇了哪個選項,因此這邊用”didSelectRow”來偵測,並加入一個label來顯示使用者選擇的選項是哪個,這個function會得到被選擇的component及row號碼。

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
resultLabel.text = "row:\(row), component:\(component)"
}
用didSelectRow得到使用者選擇選項的row及component號碼

多個component的pickerView

這邊將上述程式碼中的”numberOfComponents”由1改為3。

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 3
}

由於“numberOfRowsInComponent”及“titleForRow”的程式碼都沒有變,因此每一排仍會顯示0~9的數字作為選項。這邊同樣使用didSelectRow來顯示目前選擇的選項,可以看到滑動不同排的時候component的編號也會跟著改變。

多個component的pickerView

實作日本地名選擇的pickerView

練習實作一個選擇日本地區後,會顯示該地區所有地名的pickerView,並將選擇的地名印在下方的label中。

日本地名選擇的pickerView

首先需要有日本地名資料,這邊設計一個struct來儲存各個地區的縣市選項。

struct Area {
let name: String
let states: [String]
}

並將資料存到areas的array中。

let areas = [
Area(name: "北海道", states: ["北海道"]),
Area(name: "東北", states: ["青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県"]),
Area(name: "北関東", states: ["栃木県", "群馬県", "茨城県"]),
Area(name: "首都圏", states: ["東京都", "埼玉県", "千葉県", "神奈川県"]),
Area(name: "甲信越", states: ["山梨県", "長野県", "新潟県"]),
Area(name: "中部", states: ["富山県", "石川県", "福井県"]),
Area(name: "東海", states: ["静岡県", "岐阜県", "愛知県", "三重県"]),
Area(name: "近畿", states: ["京都府", "大阪府", "滋賀県", "兵庫県", "奈良県", "和歌山県"]),
Area(name: "中国", states: ["鳥取県", "島根県", "岡山県", "広島県", "山口県"]),
Area(name: "四国", states: ["徳島県", "香川県", "愛媛県", "高知県"]),
Area(name: "九州", states: ["福岡県", "佐賀県", "長崎県", "大分県", "熊本県", "宮崎県", "鹿児島県"]),
Area(name: "沖縄", states: ["沖縄県"])
]

另外定義selectedArea的變數來存取目前選取的地區。

var selectedArea: Area = areas[0]

同樣用extension遵從UIPickerViewDelegate, UIPickerViewDataSource,並寫下列的function。

這邊設定兩個component,一個用來顯示地區選項,一個顯示地名。

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}

指定兩個component顯示的選項數量。
當component=0時為地區選單,因此回傳地區的數目(areas.count)。
當component=1時則為地名選單,會回傳所選擇地區所包含地名數目(selected.states.count)。

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return areas.count
} else {
return selectedArea.states.count
}
}

指定選項顯示的title。
地區選單(component=0)會顯示每個地區的名稱
地名選單(component=1)會顯示所選擇地區包含的地名名稱

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return areas[row].name
} else {
return selectedArea.states[row]
}
}

最後用didSelectRow偵測目前所選擇的地名,並顯示在下方label中。
這邊當使用者滑動地名選單(component=1)時會直接印出地名,但如果是滑動地區選單時,則用reloadComponent來重新顯示所選擇地區的地名選項列表,並用selectRow讓地名選單回到第一個row的位置,印出第一個地名。

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
resultLabel.text = "row:\(row), component:\(component)"
if component == 0 {
selectedArea = areas[row]
pickerView.reloadComponent(1)
pickerView.selectRow(0, inComponent: 1, animated: true)
resultLabel.text = selectedArea.states[0]
} else {
resultLabel.text = selectedArea.states[row]
}
}

--

--