我正在开发一个需要在Firebase数据库中存储国家和城市的应用程序。 除了存储,我还需要检索该信息并在pickerView中呈现给用户。鉴于此,我需要从数据库中读取国家和城市,检查它们的索引并在pickerView中进行设置。
国家和城市存储在JSON中
{
"Country": [
{
"name": "UK",
"cities": [
{
"name": "London"
},
{
"name": "Manchester"
},
{
"name": "Bristol"
}
]
},
{
"name": "USA",
"cities": [
{
"name": "New York"
},
{
"name": "Chicago"
}
]
},
{
"name": "China",
"cities": [
{
"name": "Beijing"
},
{
"name": "Shanghai"
},
{
"name": "Shenzhen"
},
{
"name": "Hong Kong"
}
]
}
]
}
我读取JSON的代码是
// Declared in Class
var countryList = [NSDictionary]()
var selectedRow = [NSDictionary]()
var selectedCity = ""
var selectedCountry = ""
func readJson() {
if let path = Bundle.main.path(forResource: "Countries", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
if let jsonResult = jsonResult as? Dictionary<String, AnyObject>, let country = jsonResult["Country"] as? [NSDictionary] {
//handles the array of countries on your json file.
self.countryList = country
self.selectedRow = self.countryList.first?.object(forKey: "cities") as! [NSDictionary]
}
} catch {
print("error loading countries")
// handle error
}
}
}
上面的代码使我可以提供带有2个会话的UIPickerView以及国家(地区)和该国家(地区)内的城市列表。从那里,我还可以确定选择了哪个国家和城市。
作为我的代码的一部分,我有一个函数,可以让我识别UIPickerView中保存的Country(countryIndex)和City(cityIndex)的索引是什么,以便我进行设置,这就是问题的开始
func indexOf(city: String, inCountry country: String, in countries: [Country]) -> (countryIndex: Int, cityIndex: Int)? {
// countries is an array of [Country]
// var countries = [Country]()
guard let countryIndex = countries.firstIndex(where: {$0.name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {$0.name == city}) else {return nil}
//let cityIndex = 0
return (countryIndex, cityIndex)
} // courtesy of @flanker
当我的国家和城市存储到[国家/地区]时,此功能运行得非常好,但是不适用于JSON的NSDictionary。
我试图 1)通过[NSDictionary]更改[Country],通过“ countryList”更改“ countries”,并通过“ Country”更改“ name” 在这里,我收到错误消息“ NSDictionary没有会员国” 我还尝试仅留下$ 0 ==不能正常运行的国家/地区。 2)也尝试过“ countryList.firstIndex(of:“ USA”)“,但出现以下错误 无法将“字符串”类型的值转换为预期的参数类型“ NSDictionary”
任何人都可以提供帮助吗?如何使func indexOf重新工作?
谢谢
已根据@vadian的建议进行了更新
我更新的代码是
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
@IBOutlet weak var pickerView: UIPickerView!
@IBOutlet weak var countryLbl: UILabel!
var familyNames: [String] = []
var fontName = "Arial"
let fontCount = 0
var countryList = [Country]()
var selectedRow = [City]()
var selectedCity : City?
var selectedCountry : Country?
struct Root : Decodable {
let country : [Country] // better plural let countries
private enum CodingKeys : String, CodingKey { case country = "Country" }
}
struct Country : Decodable {
var name : String
var cities : [City]
}
struct City : Decodable {
var name : String
}
override func viewDidLoad() {
super.viewDidLoad()
pickerView.delegate = self
pickerView.dataSource = self
fontName = "HelveticaNeue"
}
func indexOf(city: String, inCountry country: String, in countries: [Country]) -> (countryIndex: Int, cityIndex: Int)? {
guard let countryIndex = countries.firstIndex(where: {$0.name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {$0.name == city}) else {return nil}
return (countryIndex, cityIndex)
}
func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
if component == 0 {
return 80
} else {
return 300
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return countryList.count
} else {
return selectedRow.count
}
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
var rowTitle = ""
let pickerLabel = UILabel()
pickerLabel.textColor = UIColor.blue
switch component {
case 0:
rowTitle = countryList[row].name
case 1:
rowTitle = selectedRow[row].name
default:
break
}
pickerLabel.text = rowTitle
pickerLabel.font = UIFont(name: fontName, size: 20.0)
pickerLabel.textAlignment = .center
return pickerLabel
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
pickerView.reloadAllComponents()
if component == 0 {
self.selectedCountry = self.countryList[row]
self.selectedRow = self.countryList[row].cities
pickerView.reloadComponent(1)
self.pickerView.selectRow(0, inComponent: 1, animated: true)
self.selectedCity = self.selectedRow[0]
} else {
self.selectedCity = self.selectedRow[row]
}
if let indexes = indexOf(city: self.selectedCity!.name, inCountry: self.selectedCountry!.name, in: countryList) {
//do something with indexes.countryIndex and indexes.cityIndex
print("This is the result \(indexes.cityIndex) and \(indexes.countryIndex)")
}
countryLbl.text = "The right answer is: \(self.selectedCountry?.name) and the city is \(self.selectedCity?.name)"
}
func readJson() {
let url = Bundle.main.url(forResource: "Countries", withExtension: "json")!
do {
let data = try Data(contentsOf: url)
let jsonResult = try JSONDecoder().decode(Root.self, from: data)
//handles the array of countries on your json file.
self.countryList = jsonResult.country
self.selectedRow = self.countryList.first!.cities
} catch {
print("error loading countries", error)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
readJson()
}
}
答案 0 :(得分:1)
您还必须将分发包中的JSON解码为自定义结构
struct Root : Decodable {
let country : [Country] // better plural let countries
private enum CodingKeys : String, CodingKey { case country = "Country" }
}
struct Country : Decodable {
let name : String
let cities : [City]
}
struct City : Decodable {
let name : String
}
var countryList = [Country]()
var selectedRow : [City]()
var selectedCity : City?
var selectedCountry : Country?
func readJson() {
let url = Bundle.main.url(forResource: "Countries", withExtension: "json")!
do {
let data = try Data(contentsOf: url)
let jsonResult = try JSONDecoder().decode(Root.self, from: data)
//handles the array of countries on your json file.
self.countryList = jsonResult.country
self.selectedRow = self.countryList.first!.cities
} catch {
print("error loading countries", error)
}
}
然后您的方法indexOf(city:inCountry:in:)
有效
由于文件位于应用程序捆绑包中,因此请考虑忽略根字典"Country"
并解码[Country].self
。
通常的旁注:
请勿在Swift中使用NS...
集合类型。您丢弃了类型信息。使用本机类型。
.mutableContainers
和.mutableLeaves
在Swift中毫无意义。具有讽刺意味的是,您无论如何都要将值分配给 im 可变常量。
Swift 3+中的JSON字典始终是值类型[String:Any]
而不是引用类型[String:AnyObject]
。