【问题标题】:Waiting until an asynchronous call is complete before running the "override" functions of a View Controller (Swift)在运行视图控制器(Swift)的“覆盖”功能之前等待异步调用完成
【发布时间】:2020-01-02 00:05:11
【问题描述】:

我正在创建一个应用程序,它从 Alpha Vantage 获取股票数据并显示它,作为 Swift 的第一次尝试(我通常使用 JS 开发)。

更准确地说,我有一个表格视图,每个单元格都有以下标签:

  • “打开”价格标签
  • “高”价格标签
  • “低价”标签
  • “关闭”价格标签
  • “音量”标签

这是我用于单元格的自定义类。

    import UIKit

    class StockChoiceCell: UITableViewCell {

        @IBOutlet weak var openPriceLabel: UILabel!
        @IBOutlet weak var highPriceLabel: UILabel!
        @IBOutlet weak var lowPriceLabel: UILabel!
        @IBOutlet weak var closePriceLabel: UILabel!
        @IBOutlet weak var volumeLabel: UILabel!

    }

我有一个异步函数,它成功地从 Alpha Vantage 检索所需数据并返回 [DataPoint],其中 DataPoint 是一个包含开盘价、最高价、最低价和收盘价以及交易量的结构。我说成功,因为我在应用程序的另一个方面使用该功能没有问题。

该函数类似于:

func retrieveDataFromAPI (tic: String, completion: @escaping ([DataPoint])->()) {

// Fetch stuff

// Pack it under the DataPoint structure

// ...

completion(arrayOfDataPoints)

}

这就是问题所在。

由于函数的异步特性,我不知道如何让应用程序在运行前“等待”数据获取完成:

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

        // ... Setting stuff, etc.

        return cell
    } 

我在网上搜索,主要尝试以下两个帖子,因为我的尝试之一是将异步函数嵌套在另一个同步函数中:

如果这是 JS,我会立即使用 Promise。但 Swift 对我来说是新手,我仍在探索异步函数在该语言中是如何工作的。

谢谢!

【问题讨论】:

  • .reloadData() 上的UITableView 是否可以满足您的需求?当您获得请求中的所有数据时,只需调用它即可。

标签: ios swift uitableview asynchronous


【解决方案1】:

由于函数的异步特性,我不知道如何让应用程序“等待”数据获取完成后再运行cellForRowAtIndexPath

好吧,cellForRowAtIndexPath 将自动“不执行”,当没有数据提供给表视图时。请记住,这是一个表格视图数据源方法。你不能直接调用它。所以只需像往常一样实现cellForRowAtIndexPath。如果numberOfRowsInSection 返回0,那么cellForRowAtIndexPath 就不会被调用,对吗?

我想你已经用这样的一行实现了numberOfRowsInSection

return dataSource.count

其中dataSource 是您的VC 的属性,类型为[DataPoint]。最初,将此dataSource 属性设置为一个空数组:

var dataSource = [DataPoint]()

当表格视图加载时,它不会显示任何内容,因为尚未获取数据。现在在viewDidLoad,我们调用这个异步函数来获取数据:

retrieveDataFromAPI(tic: "some string") {
    dataFetched in
    DispatchQueue.main.async {
        self.dataSource = dataFetched
        self.tableView.reloadData() // this calls the table view data source methods again
    }
}

在您调用reloadData 时,dataSource 将不再为空,并且将为适当的索引路径调用cellForRowAtIndexPath


请注意,Swift 有 PromiseKit,您可能觉得熟悉,也可能不熟悉。

【讨论】:

  • 成功了!您的代码运行良好,并且当数组为空时不调用 cellForRowAtIndexPath 的要点(@Sh_Khan 也提出了这一点)对我来说也非常有趣和有用。谢谢大家!
【解决方案2】:

您可以在请求之前隐藏表格并显示活动指示符,然后在完成成功块内显示它,顺便说一句,numberOfRowsInSection 返回零时不会调用cellForRowAt(表格数据源数组的初始计数)在响应返回之前加载表时

retrieveDataFromAPI(tic:<##>) { res in
 DispatchQueue.main.async {
    self.arr = res
    self.tableView.reloadData()
 }
}

【讨论】:

  • 感谢您的回复。您和@George_E 提到使用.reloadData(),这确实看起来很有希望。但是,如何将 result 数据设置为标签?我需要在tableView cellForRowAt 函数中使用这个 sn-p 吗?
猜你喜欢
  • 1970-01-01
  • 2016-06-28
  • 2018-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 2018-10-18
  • 1970-01-01
相关资源
最近更新 更多