NSDictionary firstIndex 标识Swift 5
NSDictionary firstIndex identification Swift 5
我正在开发一个应用程序,我需要在 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
}
}
}
上面的代码允许我为 UIPickerView 提供 2 个会话以及国家和该国家内的城市列表。从那里,我还可以确定选择了哪个国家和城市。
作为我代码的一部分,我有一个函数可以让我识别 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: {[=13=].name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {[=13=].name == city}) else {return nil}
//let cityIndex = 0
return (countryIndex, cityIndex)
} // courtesy of @flanker
当我的国家和城市存储到 [国家] 但不适用于来自 JSON.
的 NSDictionary 时,此功能运行良好
我试过了
1) 通过 [NSDictionary] 更改 [Country],通过 "countryList" 更改 "countries",通过 "Country" 更改 "name"
在这里我收到错误 "NSDictionary has no member Country"
我还试图只留下 $0 == Country ,但效果不佳。
2) 也试过 "countryList.firstIndex(of: "USA")" 但出现以下错误
无法将类型 'String' 的值转换为预期的参数类型 '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: {[=14=].name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {[=14=].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()
}
}
您还必须将捆绑包中的 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 可变常量。
A JSON字典在Swift 3+总是值类型[String:Any]
不是引用类型[String:AnyObject]
.
我正在开发一个应用程序,我需要在 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
}
}
}
上面的代码允许我为 UIPickerView 提供 2 个会话以及国家和该国家内的城市列表。从那里,我还可以确定选择了哪个国家和城市。
作为我代码的一部分,我有一个函数可以让我识别 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: {[=13=].name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {[=13=].name == city}) else {return nil}
//let cityIndex = 0
return (countryIndex, cityIndex)
} // courtesy of @flanker
当我的国家和城市存储到 [国家] 但不适用于来自 JSON.
的 NSDictionary 时,此功能运行良好我试过了 1) 通过 [NSDictionary] 更改 [Country],通过 "countryList" 更改 "countries",通过 "Country" 更改 "name" 在这里我收到错误 "NSDictionary has no member Country" 我还试图只留下 $0 == Country ,但效果不佳。 2) 也试过 "countryList.firstIndex(of: "USA")" 但出现以下错误 无法将类型 'String' 的值转换为预期的参数类型 '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: {[=14=].name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {[=14=].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()
}
}
您还必须将捆绑包中的 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 可变常量。A JSON字典在Swift 3+总是值类型
[String:Any]
不是引用类型[String:AnyObject]
.