2016-04-07 44 views
1

我有一個由三部分組成的表格視圖。一部分從前一個視圖控制器傳入,但另外兩部分需要異步加載。我在等待HTTP響應的區域顯示「佔位符加載調試器」。當一個部分返回時,我嘗試更新表數據,但是我發現我可以進入一種情況,即兩個響應同時返回並嘗試同時更新表,導致崩潰。看起來我需要應用某種鎖和隊列,以便它不會同時嘗試更新表的多個異步請求崩潰。如何異步加載和更新UITableView的幾個部分?

我想知道,什麼是iOS的最佳做法是安全地加載/更新UITableView的部分部分異步。我不在尋找代碼示例。相反,我正在尋找用於實現此目的的術語和方法調用。

回答

1

如果您使用不同的部分(以及靜態數量的部分),請嘗試重新加載它們而不是重新加載表格視圖。當API返回時,更新其各自的部分: [self.tableView reloadSections: withRowAnimation:]

+0

這並沒有試圖回答這個問題。不要將評論發佈爲答案。 – rmaddy

+0

不要掛在「部分」上。它只是3個不同的API請求,它們在表格的三個不同部分顯示數據。是的,我認爲數據源在第一個請求完成更新表之前會被第二個請求更新。這就是爲什麼我提到了一個鎖定隊列系統,但我更關心解決這個問題的正確方法。 – Andrew

+0

@Andrew看到我更新的答案。 – HeavenlyManBR

1

簡短回答:主線程。更具體地講:

  • 更新在主線程中的數據模型
  • 在主線程
  • 刷新表視圖數據(實際上,做主線程上的所有 UI的東西,總是)

如果你這樣做,你應該沒有問題。

如果您使用的是類似NSURLConnection的東西,您可以指定接收數據時應該派發完成過程的隊列(即NSOperationQueue.mainQueue())。如果您正在執行的其他任務最終在另一個線程上執行,則可以使用類似performSelectorOnMainThreaddispatch_asyncdispatch_get_main_queue的方式將其發送回主線程。

可以重裝只是特定部分(通過reloadSections:withRowAnimation:),或者甚至只是某些行(reloadRowsAtIndexPaths:withRowAnimation:),但我不會有任何的打擾,除非/直到有一個問題(例如,降低性能或閃爍因過度重繪)。只需重新加載整個表格,直到您發現您需要做其他事情。

我知道你說你不是在尋找一個代碼示例,但我只是不能幫助自己;我的代碼溝通比言語更好。

主要事情是tableView:cellForRowAtIndexPath:,它發出一個URL請求(通過NSURLConnection)。完成進程(派發到主隊列)解析一些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) 
     } 
    } 

}