如何异步加载和更新UITableView的几个部分?

时间:2016-04-07 23:17:47

标签: ios uitableview asynchronous

我有一个由三部分组成的表格视图。一部分从前一个视图控制器传入,但另外两个部分需要异步加载。我正在展示"占位符加载微调器"在等待HTTP响应的区域中。当一个部分返回时,我尝试更新表数据,但我发现我可以进入两个响应大约在同一时间返回并尝试同时更新表的情况,从而导致崩溃。看起来我需要应用某种锁和队列,以便它不会因尝试同时更新表的多个异步请求而崩溃。

我想知道,安全加载/更新UITableView的部分部分的iOS最佳实践是异步的。我不是在寻找代码示例。相反,我正在寻找用于实现此目的的术语和方法调用。

2 个答案:

答案 0 :(得分:1)

如果您使用的是不同的部分(以及静态的部分数),请尝试重新加载它们,而不是重新加载表格视图。当API返回时,更新其相应的部分: [self.tableView reloadSections: withRowAnimation:]

答案 1 :(得分:1)

简答:主线程。更具体地说:

  • 在主线程上更新您的数据模型
  • 在主线程上重新加载表视图数据(实际上,总是在主线程上执行所有 UI内容)

如果您执行上述操作,则应该没有问题。

如果您正在使用NSURLConnection之类的内容,则可以指定在收到数据时应该向其分派完成处理的队列(即NSOperationQueue.mainQueue())。如果您正在执行其他最终在其他线程上执行的操作,则可以使用performSelectorOnMainThreaddispatch_asyncdispatch_get_main_queue等方式将主线程调度回主线程。

可以重新加载特定部分(通过reloadSections:withRowAnimation:)甚至只是某些行(reloadRowsAtIndexPaths:withRowAnimation:),但我不会为此烦恼,除非/直到出现问题(例如,由于过度重绘导致性能降低或闪烁)。开始只是重新加载整个表格,直到你发现你需要做其他事情。

我知道你说你不是在寻找代码示例,但我无法帮助自己;我在代码中的沟通比用文字沟通得好。

主要是tableView:cellForRowAtIndexPath:,它发出了一个网址请求(通过NSURLConnection)。完成proc(调度到主队列)解析一些JSON,更新模型并重新加载表。那就是它。

class ViewController: UIViewController, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    private var appIds = [ "391439366", "549762657", "568903335", "327630330", "281796108", "506003812" ]
    private var ratings = [String : Int]() // AppID : RatingCount

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.appIds.count;
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let aCell = UITableViewCell(style: .Value2, reuseIdentifier: "RatingCell")
        let appId = appIds[indexPath.row]
        aCell.textLabel?.text = appId
        if let count = self.ratings[appId] {
            // Already got rating count for this app - display it.
            aCell.detailTextLabel!.text = String(count)
            aCell.accessoryView = nil
        }
        else {
            // Don't have rating count: go get it.
            self.getNumberOfRatingsForAppID(appId) {
                success, number in
                if success {
                    // Update model and reload table.
                    self.ratings[appId] = number
                    self.tableView.reloadData()
                }
            }

            // Progress indicator while we wait for data.
            let spinner = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
            spinner.startAnimating()
            aCell.accessoryView = spinner
        }
        return aCell
    }

    typealias GetRatingsCompletion = (Bool, Int) -> ()
    func getNumberOfRatingsForAppID( appID: String, completion: GetRatingsCompletion ) {

        let appStoreURL = NSURL(string: "https://itunes.apple.com/lookup?id=\(appID)")
        let request = NSURLRequest(URL: appStoreURL!)
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue() ) {
            response, data, error in

            guard data != nil else {
                completion( false, 0 )
                return
            }

            if let
                jsonResult = (try? NSJSONSerialization.JSONObjectWithData(data!, options:[])) as? NSDictionary,
                results = jsonResult["results"] as? NSArray,
                result = results[0] as? NSDictionary,
                numberOfRatings = result["userRatingCountForCurrentVersion"] as? Int
            {
                completion( true, numberOfRatings )
                return
            }

            completion( false, 0 )
        }
    }

}