【问题标题】:Swift - TableView datasource from URLSession taskSwift - URLSession 任务中的 TableView 数据源
【发布时间】:2018-03-01 15:23:35
【问题描述】:

我必须用URLSession task 获取的一些数据填充TableView。源是一个 XML 文件,所以我将它解析到任务中。解析的结果是我传递给另一个函数的数组,该函数填充了TableView Delegates 使用的另一个数组。

我的问题是TableView Delegates 在任务结束之前被调用,所以当我启动应用程序时该表是空的,除非重新加载数据(所以我知道解析和任务工作正常)。

这里是viewDidLoad 函数。 listOfApps 是我的TableView

    override func viewDidLoad() {

            super.viewDidLoad() 

            fetchData()
            checkInstalledApps(apps: <ARRAY POPULATED>)  

            listOfApps.delegate = self
            listOfApps.dataSource = self

        }
    }

fetchData 是我获取 XML 文件并解析它的函数

    func fetchData() {
        let myUrl = URL(string: "<POST REQUEST>");
        var request = URLRequest(url:myUrl!)
        request.httpMethod = "POST"
        let postString = "firstName=James&lastName=Bond";
        request.httpBody = postString.data(using: String.Encoding.utf8);

        let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in

            self.parser = XMLParser(data: data!)
            self.parser.delegate = self

        }
        task.resume()

    }

checkInstalledApps 是我组成TableView Delegates 使用的数组的函数。

    func checkInstalledApps(apps: NSMutableArray){
    ....
    installedApps.add(...)
    installedApps.add(...)
    ....
    }

例如,设置我计数installedApps元素的行数

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    if (installedApps.count == 0) {
        noApp = true
        return 1
    }
    return installedApps.count
}

那是0。显然,如果我重新加载数据,一切正常。

我的问题是异步调用:首先我使用了一个可通过 GET 请求访问的 XML,所以我可以使用 XMLParser(contentsOf: myUrl) 并且时间不是问题。也许如果XML会长大,这样我也会有一些麻烦,但现在我必须使用POST请求

我用DispatchGroup 试过,用一个

  • group.enter()super.viewDidLoad 之前
  • group.leave()task.resume() 之后
  • group.wait()checkInstalledApps() 之后

其中组是let group = DispatchGroup(),但什么都没有。

那么,我如何告诉 tableview 委托等待任务响应和下一个函数?

提前致谢

【问题讨论】:

    标签: ios swift uitableview asynchronous


    【解决方案1】:

    我会忘记DispatchGroup,并在这里改变一种思维方式(您不想在响应出现之前冻结 UI)。

    我相信您可以将 fetchData 实现保持原样。

    XMLParserDelegate.parserDidEndDocument(_:) 中,您将收到 XML 已被解析的通知。在该方法中调用checkInstalledApps 来填充模型数据。之后只需调用listOfApps.reloadData() 告诉tableView 重新加载新数据。

    您想在主线程上同时调用checkInstalledAppslistOfApps.reloadData()(使用DispatchQueue.main.async {})。

    同时将listOfApps.delegate = selflistOfApps.dataSource = self 保留在viewDidLoad 中,就像现在一样。

    【讨论】:

      【解决方案2】:

      更简洁的方法是使用空状态视图/activityIndi​​cator/loader/progress hud(随便你),通知用户应用正在获取/加载数据,

      获取完成后,只需重新加载您的表格视图并删除空状态视图/加载器

      【讨论】:

        【解决方案3】:

        您的问题是由于您目前无法知道URLSession 任务何时结束。 reloadData() 调用几乎在提交请求后立即发生,因此您会看到空表,并且需要稍后重新加载表,尽管新的重新加载不应早于任务结束。

        以下是所发生情况的简化图:

        完成块在这里提供了一个易于实现的解决方案。您可以在下面找到一个非常简单(并且可能不完整,因为我没有关于实际 xml 解析的所有详细信息)的解决方案:

        func fetchData(completion: @escaping () -> Void) {
             let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
                    self.parser = XMLParser(data: data!)
                    self.parser.delegate = self
                    completion()
        }
        
        override func viewDidLoad() {
            super.viewDidLoad() 
            fetchData() { [weak self] in self?.tableView.reloadData() }
        }
        

        基本上完成块会完成xml数据返回链。

        【讨论】:

          猜你喜欢
          • 2021-09-01
          • 1970-01-01
          • 2017-04-05
          • 1970-01-01
          • 2019-05-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多