【问题标题】:self.tableView.reloadData() not working in Swiftself.tableView.reloadData() 在 Swift 中不起作用
【发布时间】:2014-07-29 13:18:32
【问题描述】:

我正在尝试同时学习SwiftiOS dev 的基础知识,所以请耐心等待。我有一个TableViewController,它首先解析一个本地JSON 文件并将其非常简单的数据呈现到TableViewCell 和SectionHeaderViews 中。在同一个TableViewController 中,我正在调用JSON 端点,该端点正在返回数据,然后我将其设置为变量,以便我可以访问我真正想要获取的内容(API 结构小于可取的)。所以,我最终将正确的数据设置为self.tableData,然后调用self.tableView.reloadData(),但没有任何反应。什么给了?

import UIKit

class BusinessTableViewController: UITableViewController {

    var data: NSMutableData = NSMutableData()
    var tableData: NSArray = NSArray()

    @lazy var Business: NSArray = {
        let pathTCT = NSBundle.mainBundle().pathForResource("TCT", ofType: "json")
        let data = NSData.dataWithContentsOfFile(pathTCT, options: nil, error: nil)
        return NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as NSArray
        }()

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.titleView = UIImageView(image: UIImage(named: "growler"))

        tableView.registerClass(BeerTableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.separatorStyle = .None

        fetchKimono()
    }

    override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
//        return Business.count
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        let biz = Business[section] as NSDictionary
        let results = biz["results"] as NSDictionary
        let beers = results["collection1"] as NSArray
        return beers.count
    }

    override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
        let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as BeerTableViewCell
        if let path = indexPath {
            let biz = Business[path.section] as NSDictionary
            let results = biz["results"] as NSDictionary
            let beers = results["collection1"] as NSArray
            let beer = beers[path.row] as NSDictionary

            cell.titleLabel.text = beer["BeerName"] as String
        }

        return cell
    }

    override func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String! {
        let biz = Business[section] as NSDictionary
        return biz["name"] as String
    }

    override func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
        let biz = Business[section] as NSDictionary
        let view = LocationHeaderView()
        view.titleLabel.text = (biz["name"] as String).uppercaseString
        return view
    }

    override func tableView(tableView: UITableView!, heightForHeaderInSection section: Int) -> CGFloat {
        return 45
    }

    func fetchKimono() {
        var urlPath = "names have been changed to protect the innocent"
        var url: NSURL = NSURL(string: urlPath)
        var request: NSURLRequest = NSURLRequest(URL: url)
        var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)

        connection.start()
    }

    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
        // Recieved a new request, clear out the data object
        self.data = NSMutableData()
    }

    func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
        // Append the recieved chunk of data to our data object
        self.data.appendData(data)
    }

    func connectionDidFinishLoading(connection: NSURLConnection!) {
        // Request complete, self.data should now hold the resulting info
        // Convert the retrieved data in to an object through JSON deserialization
        var err: NSError
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options:    NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        var results: NSDictionary = jsonResult["results"] as NSDictionary
        var collection: NSArray = results["collection1"] as NSArray
        if jsonResult.count>0 && collection.count>0 {
            var results: NSArray = collection as NSArray
            self.tableData = results
            self.tableView.reloadData()
        }
    }
}

【问题讨论】:

  • 所以,我的第一个错误是 Business 实际上需要是一个 NSDictionary,以匹配正在获取的 JSON。这摆脱了所有的商业变量。第二个错误是我不需要 self.tableData,我需要 Business = jsonResults——但现在我得到一个内存错误。我需要解除分配或取消初始化吗?
  • 请发布您的最新代码
  • @Dash 在下面添加了我的答案。

标签: ios objective-c json uitableview swift


【解决方案1】:

您需要通过以下方式在UI 线程上重新加载表格:

//swift 2.3
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    self.tableView.reloadData()
})

//swift 5
DispatchQueue.main.async{
    self.tableView.reloadData()
}

跟进: connection.start() 方法的一个更简单的替代方法是使用 NSURLConnection.sendAsynchronousRequest(...)

//NSOperationQueue.mainQueue() is the main thread
NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: url), queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in
    //check error
    var jsonError: NSError?
    let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &jsonError)
    //check jsonError
    self.collectionView?.reloadData()
}

这不允许您灵活地跟踪字节,例如,您可能希望通过 bytesDownloaded/bytesNeeded 计算下载进度

【讨论】:

  • 它正在调用这段代码(我正在运行println)但仍然没有重新加载数据?
  • 确保您的委托设置正确,并且您的委托方法实际上正在被调用。
  • @noobular 为什么需要 dispatch_async 我完全不明白。这个答案对我帮助很大(2 小时的谷歌搜索)。
  • @Kyslik 请注意,当您调用 connection.start() 时,会在后台线程上调用委托方法,否则会阻塞主 UI 线程并且用户体验会下降。 dispatch_async 位告诉系统在 main_queue aka ui 线程上执行重新加载。
  • 另外,不要忘记通过 ctrl+单击 TableView 并将其拖动到视图顶部的小黄色方块并选择“使 ViewController 成为情节提要上 TableView 的数据源”数据源”
【解决方案2】:

您只需输入:

首先是一个 IBOutlet:

@IBOutlet var appsTableView : UITableView

然后在Action func中:

self.appsTableView.reloadData()

【讨论】:

    【解决方案3】:

    如果您的连接在后台线程中,那么您应该像这样在主线程中更新 UI

    self.tblMainTable.performSelectorOnMainThread(Selector("reloadData"), withObject: nil, waitUntilDone: true)
    

    As I have mentioned here

    斯威夫特 4:

    self.tblMainTable.performSelector(onMainThread: #selector(UICollectionView.reloadData), with: nil, waitUntilDone: true)
    

    【讨论】:

    • 这为我解决了问题。为什么 self.collectionView?.reloadData() 对我不起作用?我正在使用 NSURLSession
    【解决方案4】:

    在我的情况下,表格已正确更新,但没有为图像调用 setNeedDisplay(),所以我错误地认为数据没有重新加载。

    【讨论】:

      【解决方案5】:

      所以,问题在于我试图不恰当地使用@lazy,这导致我的业务变量本质上是一个常量,因此无法编辑。另外,我现在只加载从 API 返回的数据,而不是加载本地 json。

      import UIKit
      
      class BusinessTableViewController: UITableViewController {
      
          var data: NSMutableData = NSMutableData()
          var Business: NSMutableArray = NSMutableArray()
      
          override func viewDidLoad() {
              super.viewDidLoad()
      
              navigationItem.titleView = UIImageView(image: UIImage(named: "growler"))
      
              tableView.registerClass(BeerTableViewCell.self, forCellReuseIdentifier: "cell")
              tableView.separatorStyle = .None
      
              fetchKimono()
          }
      
          override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
              return Business.count
          }
      
          override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
              if (Business.count > 0) {
                  let biz = Business[section] as NSDictionary
                  let beers = biz["results"] as NSArray
                  return beers.count
              } else {
                  return 0;
              }
          }
      
          override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
              let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as BeerTableViewCell
              if let path = indexPath {
                  let biz = Business[path.section] as NSDictionary
                  let beers = biz["results"] as NSArray
                  let beer = beers[path.row] as NSDictionary
      
                  cell.titleLabel.text = beer["BeerName"] as String
              } else {
                  cell.titleLabel.text = "Loading"
              }
      
              return cell
          }
      
          override func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
              let view = LocationHeaderView()
              let biz = Business[section] as NSDictionary
              if (Business.count > 0) {
                  let count = "\(Business.count)"
                  view.titleLabel.text = (biz["name"] as String).uppercaseString
              }
              return view
          }
      
          override func tableView(tableView: UITableView!, heightForHeaderInSection section: Int) -> CGFloat {
              return 45
          }
      
          func fetchKimono() {
              var urlPath = "names have been removed to protect the innocent"
              var url: NSURL = NSURL(string: urlPath)
              var request: NSURLRequest = NSURLRequest(URL: url)
              var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
      
              connection.start()
          }
      
          func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
              // Recieved a new request, clear out the data object
              self.data = NSMutableData()
          }
      
          func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
              // Append the recieved chunk of data to our data object
              self.data.appendData(data)
          }
      
          func connectionDidFinishLoading(connection: NSURLConnection!) {
              // Request complete, self.data should now hold the resulting info
              // Convert the retrieved data in to an object through JSON deserialization
              var err: NSError
              var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
              var results: NSDictionary = jsonResult["results"] as NSDictionary
              var collection: NSArray = results["collection1"] as NSArray
              if jsonResult.count>0 && collection.count>0 {
                  Business = jsonResult
                  tableView.reloadData()
              }
          }
      }
      

      Swift Docs on @lazy

      您必须始终将惰性属性声明为变量(使用 var 关键字),因为在实例初始化完成之前可能无法检索其初始值。常量属性在初始化完成之前必须始终有一个值,因此不能声明为惰性。

      【讨论】:

        【解决方案6】:

        除了来自 UI/Main Thread 的明显 reloadData(无论 Apple 怎么称呼它),就我而言,我忘记更新 SECTIONS 信息。因此它没有检测到任何新的部分!

        【讨论】:

          【解决方案7】:

          所有对 UI 的调用都应该是异步的,您在 UI 上所做的任何更改(例如更新表格或更改文本标签)都应该从主线程完成。 使用 DispatchQueue.main 会将您的操作添加到主线程上的队列中。

          斯威夫特 4

          DispatchQueue.main.async{
              self.tableView.reloadData()
          }
          

          【讨论】:

            【解决方案8】:

            您必须仅在主线程中重新加载您的 TableView。 否则您的应用将崩溃或在一段时间后更新。对于每次 UI 更新,建议使用主线程。

            //To update UI only this below code is enough
            //If you want to do changes in UI use this
            DispatchQueue.main.async(execute: {
                //Update UI
                self.tableView.reloadData()//Your tableView here
            })
            
            //Perform some task and update UI immediately.
            DispatchQueue.global(qos: .userInitiated).async {  
                // Call your function here
                DispatchQueue.main.async {  
                    // Update UI
                    self.tableView.reloadData()  
                }
            }
            
            //To call or execute function after some time and update UI
            DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
                //Here call your function
                //If you want to do changes in UI use this
                DispatchQueue.main.async(execute: {
                    //Update UI
                    self.tableView.reloadData()
                })
            }
            

            【讨论】:

              【解决方案9】:

              试试看:tableView.reloadSections(IndexSet(integersIn: 0...0), with: .automatic) 它对我有帮助

              【讨论】:

                【解决方案10】:

                我也遇到了同样的问题,我做错的是我忘记添加了

                tableView.delegate = self    
                tableView.dataSource = self
                

                在 viewDidLoad() {} 方法中。这可能是 self.tableView.reloadData() 不起作用的原因之一。

                【讨论】:

                  猜你喜欢
                  • 2015-12-06
                  • 2015-09-16
                  • 2014-08-05
                  • 2014-07-27
                  • 2016-02-18
                  • 2015-03-19
                  • 2015-02-18
                  • 2018-10-11
                  • 1970-01-01
                  相关资源
                  最近更新 更多