Чтобы упростить задачу, мы можем создать некоторые структуры для представления стран и штатов.
struct Country {
struct State {
let country: String
let name: String
}
let name: String
let states: [State]
static var ref: DatabaseReference {
return Database.database().reference(withPath: "countries")
}
static func from(key: String, value: Any?) -> Country? {
guard
let value = value as? [String:[String]],
let states = value["states"] else { return nil }
return Country(name: key, states: states.map {
State(country: key, name: $0)
})
}
static func countries(from snapshot: DataSnapshot) -> [Country]? {
return snapshot.snapshots.compactMap {
Country.from(key: $0.key, value: $0.value)
}
}
}
И расширение DataSnapshot
, чтобы предоставить нам удобный доступ к дочерним элементам снимка.
extension DataSnapshot {
var snapshots: [DataSnapshot] {
return children.allObjects as? [DataSnapshot] ?? []
}
}
Затем мы создаем некоторые свойства для обработки стран - массив для их хранения и некоторые вычисляемые свойства для возврата выбранной страны и штата.
class CountryStatePickerViewController: UIViewController {
// MARK: Outlets
@IBOutlet weak var pickerView: UIPickerView! {
didSet {
loadCountries()
}
}
// MARK: Properties
private var countries = [Country]() {
didSet {
pickerView.reloadAllComponents()
}
}
var selectedCountry: Country? {
return country(at: pickerView.selectedRow(inComponent: 0))
}
var selectedState: Country.State? {
return selectedCountry.flatMap {
state(at: pickerView.selectedRow(inComponent: 1), in: $0)
}
}
}
Нам также нужно несколько методов для безопасного доступа к странам и штатам по определенному индексу, а также один для загрузки стран в первую очередь.
extension DataSnapshot {
var snapshots: [DataSnapshot] {
return children.allObjects as? [DataSnapshot] ?? []
}
}
extension CountryStatePickerViewController {
private func loadCountries() {
Country.ref.observeSingleEvent(of: .value, with: { [weak self] in
self?.countries = Country.countries(from: $0) ?? []
})
}
private func country(at index: Int) -> Country? {
return countries.indices.contains(index) ? countries[index] : nil
}
private func state(at index: Int, in country: Country) -> Country.State? {
return country.states.indices.contains(index) ? country.states[index] : nil
}
}
Наконец, в наших UIPickerView
протокольных соответствиях массив стран заполняет источник данных. Мы можем использовать два компонента для представления стран и соответствующих им государств.
extension CountryStatePickerViewController: UIPickerViewDataSource, UIPickerViewDelegate {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch component {
case 0:
return countries.count
case 1:
return selectedCountry?.states.count ?? 0
default:
return 0
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
switch component {
case 0:
return country(at: row)?.name
case 1:
return selectedCountry.flatMap { state(at: row, in: $0)?.name }
default:
return nil
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 { pickerView.reloadComponent(1) }
selectedState.flatMap { print($0.name, $0.country) }
}
}