【问题标题】:How can I get value of a Dictionary item from first item of an Array in Swift?如何从 Swift 中数组的第一项中获取字典项的值?
【发布时间】:2019-03-28 06:35:29
【问题描述】:

我在下面定义了 Dictionary in an Array 对象:

var dataList : [[String:Any]]?

然后我将一些数据加载到其中。这是我在下面运行此代码时的输出:

print("DATA LIST:\n \(myRecordList)")

输出:

Optional([["itemCode": 0, "itemText": please select city], ["itemCode": 1, "itemText": City A], ["itemCode": 2, "itemText": City B], ["itemCode": 3, "itemText": City C], ["itemCode": 4, "itemText": City D], ["itemCode": 5, "itemText": City E], ["itemCode": 6, "itemText": City F]])

我想从数组中的第一项获取字典的值:

如您所知,这里是数组中的第一项:

["itemCode": 0, "itemText": 请选择城市]

这是上面字典中的值:

"itemText": 请选择城市

如果dataList计数不为零,我想将上面的文本(“请选择城市”)设置为按钮标题:

if dataList?.count != 0
{
    **//need help in here!!!**
    {
        self.btnSelectMarkaOutlet.setTitle(tempBtnTitle, for: .normal)
    }
}

我已经尝试过以下范围。但它粉碎了:

    if let newTry = dataList![0]["itemText"] as? [String]
{
    print("here is the output:: \(newTry[0])")
}

这是错误:

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

让 tempBtnTitle = 必须是“请选择城市” 我想这样做。

让我分享整个视图控制器文件。因为 dataList 是 nil。

import UIKit

class SinyalSeviyesineGoreAramaYapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var btnSelectMarkaOutlet: UIButton!
    @IBOutlet weak var pickerViewOutlet: UIPickerView!

    var dataList : [[String:Any]]?

    override func viewDidLoad() {
        print("viewDidLoad")
        super.viewDidLoad()

        self.pickerViewOutlet.delegate = self
        self.pickerViewOutlet.dataSource = self

        self.loadSayacRecordsForTheUIPickerView()

        print("Is the data list nil? : \(self.dataList)")
        if let newTry = self.dataList![0]["itemText"] as? String
        {
            print("here is the output:: \(newTry)")
        }

        //I WANTED TO SET BUTTON TITLE !!
        let tempButtonTitle = ""
        self.btnSelectMarkaOutlet.setTitle(tempButtonTitle, for: .normal)
    }


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        let recordName = self.dataList![row]
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

        //return self.cityList![row]

        let info = self.dataList![row]

        if let name = info["itemCode"] as? String,
            let code = info["itemText"] as? String
        {
            let text = "\(name) - \(code)"
            //print("am i here? \(text)")
            return text
        }

        return nil
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return self.dataList == nil ? 0 : self.dataList!.count
    }

    func loadSayacRecordsForTheUIPickerView()
    {
        print("loadSayacRecordsForTheUIPickerView method is called")

        ServiceManager.oServiceManager.loadSayacRecords()
            {
                (result) in

                if let sayacRecords = try? JSONSerialization.jsonObject(with: result!, options: .mutableContainers) as? [String:Any]
                {
                    if let resultCodeFromWebApi = sayacRecords?["resultCode"] as? String
                    {
                        DispatchQueue.main.async
                            {
                                print("loadSayacRecordsForTheUIPickerView resultCode: \(resultCodeFromWebApi)")

                                if resultCodeFromWebApi == "999"
                                {
                                    if let myRecordList = sayacRecords?["recordList"] as? [[String:Any]]
                                    {
                                        //DispatchQueue.main.async
                                        //{
                                        self.dataList = myRecordList
                                        print("DATA LIST:\n \(self.dataList)")
                                        self.pickerViewOutlet.reloadAllComponents()
                                        //}
                                    }
                                } // resultCodeFromWebApi == 999 ENDS
                                else
                                {
                                    if let resultMessageFromWebApi = sayacRecords?["resultMessage"] as? String
                                    {
                                        print("resultMessage: \(resultMessageFromWebApi)")
                                    }
                                }
                        } // DispatchQueue.main.async ENDS
                    }
                    else
                    {
                        print("cant parse it")
                    } // if let resultCodeFromWebApi = loginResult?["resultCode"] as? Int ENDS
                }
        }
    } // loadSayacRecordsForTheUIPickerView ENDS

} // class SinyalSeviyesineGoreAramaYapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource ENDS

【问题讨论】:

  • dataList 显然是零,你需要as? String 而不是as? [String]
  • 亲爱的@Sweeper dataList 是零。这就是我共享整个 viewController 文件的原因。你能看看为什么dataList是nil吗?我在 'print("Is the data list nil? : (self.dataList)")'之前调用 loadSayacRecordsForTheUIPickerView()
  • 为什么这个问题用 swift5 标记?在我看来,您在异步调用完成之前正在访问dataList
  • Swift 5 是个错误。谢谢。我在 loadSayacRecordsForTheUIPickerView() 函数中使用了 DispatchQueue.main.async。 DispatchQueue.main.async 对于异步问题不是很好吗?我该如何解决这个问题?
  • 您应该使用完成处理程序(闭​​包)来更新您的按钮,例如 this question

标签: swift xcode


【解决方案1】:

问题在于您实现的逻辑:dataList 是异步加载的,这意味着您不知道它何时可用,但您使用它就像它始终可用一样。

这个错误的例子在viewDidLoad:

self.loadSayacRecordsForTheUIPickerView()

print("Is the data list nil? : \(self.dataList)")
if let newTry = self.dataList![0]["itemText"] as? String
{
    print("here is the output:: \(newTry)")
}

从第一行开始加载dataList,但在for 循环中,您将强制展开,这意味着您在说“嗨,我100% 确定它会可用”——这不是真的。

您的问题的一个可能解决方案是将dataList! 转换为dataList?,因此如果未加载,则不会发生任何事情,您的应用也不会崩溃。

更好更快的解决方案是为dataList 设置一个默认值,这样在未加载的情况下,您只会对空数据模型做出反应:

var dataList : [[String:Any]] = [[String:Any]]()

【讨论】:

  • 亲爱的@Marco Pace 我像你写的那样定义它: var dataList : [[String:Any]] = [[String:Any]]() 之后我改变了dataList?像你说的到处都是。但它在代码行之后给了我一个错误:'self.loadSayacRecordsForTheUIPickerView()' 这是错误:线程 1:致命错误:索引超出范围(我认为它说 dataList 为 nil。为什么我之后仍然是 nil调用 self.loadSayacRecordsForTheUIPickerView() 函数?如何异步修复这个问题?我在函数中写了 DispatchQueue.main.async。这不是异步修复问题吗?
  • 这是一个不同的错误:您现在有一个 emtpy 数组,这意味着它包含 0 个元素,但是您正在获取没有安全检查的元素,例如 self.dataList[0] 行:您正在尝试在索引 0 处获取元素,但您没有任何索引,因此您的程序崩溃。为避免崩溃,您应该始终检查arrat 是否有足够的元素来获取所需的索引。示例:如果 dataList.count > 0 { let variable = dataList[0] }。添加此检查将确保您的应用不会崩溃。
  • 明白了。但我不明白为什么 dataList 没有加载。我首先调用 self.loadSayacRecordsForTheUIPickerView() 方法,然后检查 dataList。在我的逻辑中,这里有一个异步案例。我怎样才能解决这个问题?如果可能的话,你能给我写个正确的逻辑来从 web api 加载选择器。并将“选择城市”文本设置为按钮标题。
  • 你的逻辑是完全正确的,没有什么要修复的:异步的想法是你没有数据,你不知道它们什么时候可用,所以你需要添加您的代码中的一些安全性。 - 当一切都立即可用时:您可以立即使用它 - 当需要检索数据时(从 API、数据库或其他东西):您需要在代码中实现一些安全性以处理这种不确定的情况。这是一个不同的问题,但您的逻辑(数据可用时重新加载选择器)是正确的。每个应用程序都这样做,这就是为什么你有加载轮子:)
  • 我创建了另一个函数。然后,如果我可以加载 dataList,我在函数 loadSayacRecordsForTheUIPickerView 中调用了这个新函数。然后它起作用了。我想我遇到了异步问题。
【解决方案2】:

一个原因是数组为 nil,而且您使用 [String] 展开,它是 String,它可能给出 nil。 请尝试以下操作以避免崩溃。

if dataList?.isEmpty == false,let firstDic = dataList?.first, let neededString = firstDic["itemText"] as? String   {
}

【讨论】:

  • 是的,数据列表为零。我已经扩展了这个问题。我在我的问题中的这个粗体句子之后共享了整个视图控制器文件:'让我共享整个视图控制器文件。因为 dataList 是零。为什么dataList是零?怎么会?我在 viewDidLoad 中打印之前加载它。
  • 亲爱的Pranith,您的回复是一个解决方案。但是我们注意到 dataList 是 nil。所以我有“异步”问题。我不想针对这个问题发布另一个问题。如果可能的话,你能为我写异步问题的解决方案吗?因为在 ViewDidLoad 中调用 self.loadSayacRecordsForTheUIPickerView 方法后 dataList 仍然为零。我在这个函数中使用了 DispatchQueue.main.async。但是仍然存在异步问题。在我调用 self.loadSayacRecordsForTheUIPickerView 方法后必须加载数据列表。然后我必须尝试从加载的对象中读取 itemText。
猜你喜欢
  • 2021-02-13
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 2017-10-31
  • 1970-01-01
相关资源
最近更新 更多