NSDictionary firstIndex标识Swift 5

问题描述 投票:0回答:1

我正在开发一个需要在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()
    }
}
json swift nsdictionary swift5
1个回答
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]

© www.soinside.com 2019 - 2024. All rights reserved.