ios - 如何将 Firebase 数据的节点放置在数组中
问题描述
在我的应用程序的第一页上,我使用了 UIViewPicker,它允许用户选择他们想要选择的国家。而不是我通过数组将国家名称硬编码到 UIPicker 中(如下所示):
var country = ["USA", "CANADA"]
我决定最好让应用程序访问我的 firebase 数据库,在那里它可以获取所有国家/地区,并将它们附加到数组中,UIPicker 将从中读取。
这样,如果我想添加更多国家/地区,我就不必发布新版本的应用程序。我可以简单地在数据库中添加另一个国家,应用程序会自动更新它。但我不太确定如何做到这一点。当我打印数据库的快照时,我得到了整个数据库,但我只想将美国和加拿大放入一个数组中。
所以这是我的firebase数据库的结构:
countries
Canada
states
Alberta
British Columbia
USA
states
Alabama
Arkansas
这是我当前的代码:
import UIKit
import FirebaseDatabase
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
@IBOutlet weak var countryPicker: UIPickerView!
//let country = ["USA", "Canada"] I am no longer using this hardcoded array
var countrySelected = String()
var action = 0
override func viewDidLoad() {
super.viewDidLoad()
//this is where I reach out to the database
let database = Database.database().reference()
database.child("countries").observeSingleEvent(of: .value) { (Snapshot) in
print(Snapshot)
}
//add code here that takes the snapshot and appends the the array below
var country = string()
countrySelected = country[0]
countryPicker.selectRow(action, inComponent: 0, animated: false)
//print(countrySelected)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return country.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return country[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
countrySelected = country[row]
action = country.firstIndex(of: countrySelected)!
//print(countrySelected)
}
}
如果有人可以帮助解释,那就太棒了!
解决方案
为了使事情更容易,我们可以创建一些结构来表示国家和州。
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) }
}
}
推荐阅读
- java - 为什么“==”应该是 .equals() 比较数组列表中的字符串?
- node.js - 如何在 Gatsby 中将 http 重定向到 https?
- javascript - 在 setState 之后反应原生不重新渲染
- python-3.x - 如何使用forloop并排绘制图表
- reactjs - 像“useLocation”这样的反应路由器钩子的存根不起作用
- angular - 角度文件上传传递图像作为[对象文件]
- android-studio - 在将 jsoup 与 android studio 一起使用时,我在更高的 android api 版本中收到“太多重定向错误”
- php - 从数据库查询到字符串的数组 - 非常具体的问题
- python-3.x - Using dataframe operations, determine how long, in days a given name in the dataset lived
- php - 如果您在 PHP 中退出,如何显示?