在API请求可以更新模型之前,TableView会加载单元格

时间:2019-08-10 00:43:51

标签: ios swift

我有一个tableView控制器,其中每一行将代表特定加密货币的值。我为用户想要跟踪的每种不同的加密货币提供了一个类,称为CryptoCurrency。在TableView CellforRowAt函数下,我调用了另一个名为getCryptoData的函数,该函数将使用AlamoFire发送api请求以获取每种加密货币的价格。

问题是cellForRowAt函数在getCryptoData函数可以完成并更新模型之前返回默认价格为0.00的单元格。

我假设这是因为该函数异步运行吗?

如何使它在返回单元格之前等待函数完成,还是在完成后重新加载单元格?

我尝试在tableView.reloaddata()函数的末尾添加updateCryptoData,但这导致了无限循环。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CryptoCurrencyCell", for: indexPath)        

    let cryptoCurrencyItem = cryptoCurrencyContainer.listOfCryptoCurrencies[indexPath.row]
    getCryptoData(url: getApiString(for: cryptoCurrencyItem.id), currencyItem: cryptoCurrencyItem)

    cell.textLabel?.text = cryptoCurrencyContainer.listOfCryptoCurrencies[indexPath.row].name + "\(cryptoCurrencyItem.currentPrice)"
    print(cryptoCurrencyItem.currentPrice)
    return cell
}

func getCryptoData(url: String, currencyItem: CryptoCurrency) {
    Alamofire.request(url, method: .get).responseJSON {
        response in
            if response.result.isSuccess {
                print("Sucess! bitcoin data")
                let cryptoDataJSON : JSON = JSON(response.result.value!)

                print(cryptoDataJSON)
                self.updateCryptoData(json: cryptoDataJSON, currencyItem: currencyItem)
            } else {
                print("oops")
                print("Error: \(String(describing: response.result.error))")
                //self.bitcoinPriceLabel.text = "Connection Issues"
            }
    }
}

func updateCryptoData(json : JSON, currencyItem: CryptoCurrency) {
    if let cryptoPriceResult = json["ask"].double {
        //bitcoinPriceLabel.text = String(bitcoinResult)
        currencyItem.updatePrice(price: cryptoPriceResult)
        print(currencyItem.currentPrice)
    } else {
        //bitcoinPriceLabel.text = "Price Unavailable"
        print("something aint right")
    }
}

cellForRowAt函数下,有一个打印语句:

print(cryptoCurrencyItem.currentPrice)

在单元返回之前捕获当前价格。控制台显示仍为0.00,表示getCryptoData函数尚未完成运行。

3 个答案:

答案 0 :(得分:0)

可能不是最佳解决方案,但您可以将单元格传递给方法

func getCryptoData(url: String, currencyItem: CryptoCurrency, for cell: UITableViewCell) {
    Alamofire.request(url, method: .get).responseJSON {
        response in
            if response.result.isSuccess {
                print("Sucess! bitcoin data")
                let cryptoDataJSON : JSON = JSON(response.result.value!)

                print(cryptoDataJSON)
                self.updateCryptoData(json: cryptoDataJSON, currencyItem: currencyItem, for: cell)
            } else {
                print("oops")
                print("Error: \(String(describing: response.result.error))")
                //self.bitcoinPriceLabel.text = "Connection Issues"
            }
    }

}

func updateCryptoData(json : JSON, currencyItem: CryptoCurrency, for cell: UITableViewCell) {
    if let cryptoPriceResult = json["ask"].double {
        //bitcoinPriceLabel.text = String(bitcoinResult)
        currencyItem.updatePrice(price: cryptoPriceResult)
        print(currencyItem.currentPrice)
        cell.textLabel?.text = "\(cryptoPriceResult)" // your cell
    } else {
        //bitcoinPriceLabel.text = "Price Unavailable"
        cell.textLabel?.text = "\(cryptoPriceResult)" // your cell
        print("something aint right")
    }
}

由于UITableViewCell是引用类型,因此异步调用完成后,它将更新单元格文本标签。

答案 1 :(得分:0)

您的请求正在后台线程中运行。这意味着tableView在从请求获取数据之前运行。因此,当您在ValueTask<TResult>方法中获得成功时,应在主线程中调用updateCryptoData

tableView.reloadData()

此代码将运行tableView cellForRow方法

答案 2 :(得分:0)

是的,在updateCryptoData函数末尾的tableView.reloaddata()将导致无限循环,因为在api调用后的每次重载中,您再次调用api并继续循环。

通过多种方法之一,您可以具有基于键值的数据结构。

在cellForRowAt函数中,如果标签可用,则将从数据结构中分配值。如果您的数据结构中没有可用的值,则显示一些默认值,例如“ loading”或微小的加载轮UI。

cell.priceLabel?.text = dataStructre["key"]

现在,此密钥可能是您用来标识货币项目的唯一ID。

在从单元格调用api之前,请预先检查数据结构中“ key”的值是否可用。如果值不可用,则只有您可以从数据结构中调用api else简单获取,并将其设置为Label。

if let value = dataStructre["key"]{
   cell.priceLabel?.text = value
}
else{
   callApi()
}

最后,在api调用的成功方法中,使用“密钥标识符”将您从服务器获得的值存储在数据结构中。保存到数据结构后,只需调用“ reloadData”即可。

function callApi(){
   if(success){
      dataStructure["key"] = value from server
      table.reloadData()
   }
}