当前位置: > > > Swift - 异步加载表格数据,内容不能及时显示的问题解决

Swift - 异步加载表格数据,内容不能及时显示的问题解决

(本文代码已升级至Swift3) 

1,问题描述
我们使用 tableView 的时候,又是表格内容是异步加载的。比如从网络获取数据显示、或是开启个线程队列定时刷新加载表格数据。
(1)比如我们要加载的数据如下:
[
    {
        "name": "hangge",
        "age": 100,
    },
    {
        "name": "big boss",
        "age": 1,
    },
    {
        "name": "batman",
        "age": 12,
    }
]

(2)使用 URLSession 获取远程数据后,调用 tableView的reloadData() 方法重新加载数据。
import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var ctrlnames:[AnyObject] = []
    var tableView:UITableView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建表视图
        self.tableView = UITableView(frame: self.view.frame, style:.plain)
        self.tableView!.delegate = self
        self.tableView!.dataSource = self
        //创建一个重用的单元格
        self.tableView!.register(UITableViewCell.self,
                                      forCellReuseIdentifier: "SwiftCell")
        self.view.addSubview(self.tableView!)
        
        //创建URL对象
        let urlString = "http://www.hangge.com/getData.php"
        let url = URL(string:urlString)!
        //创建请求对象
        let request = URLRequest(url: url)
        let session = URLSession.shared
        
        let dataTask = session.dataTask(with: request,
               completionHandler: {(data, response, error) -> Void in
                if error != nil{
                    print(error?.localizedDescription)
                }else{
                    self.ctrlnames = try! JSONSerialization.jsonObject(with: data!,
                        options: JSONSerialization.ReadingOptions.mutableContainers)
                        as! [AnyObject]
                    self.tableView?.reloadData()
                }
        })
        
        //使用resume方法启动任务
        dataTask.resume()
    }
    
    //在本例中,只有一个分区
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1;
    }
    
    //返回表格行数(也就是返回控件数)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.ctrlnames.count
    }
    
    //创建各单元显示内容(创建参数indexPath指定的单元)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //为了提供表格显示性能,已创建完成的单元需重复使用
        let identify:String = "SwiftCell"
        //同一形式的单元格重复使用,在声明时已注册
        let cell = tableView.dequeueReusableCell(withIdentifier: identify,
                                                 for: indexPath) as UITableViewCell
        cell.accessoryType = .disclosureIndicator
        let item = self.ctrlnames[indexPath.row]
        cell.textLabel?.text = item.object(forKey: "name") as? String
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}


(3)但会发现数据加载完毕后表格还是空白的,拖动一点点表格数据就显示出来了。 
            

2,解决办法
reloadData() 方法需要在主线程中调用,这样表格数据就能及时更新。(代码高亮出为修改的地方)
import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var ctrlnames:[AnyObject] = []
    var tableView:UITableView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建表视图
        self.tableView = UITableView(frame: self.view.frame, style:.plain)
        self.tableView!.delegate = self
        self.tableView!.dataSource = self
        //创建一个重用的单元格
        self.tableView!.register(UITableViewCell.self,
                                      forCellReuseIdentifier: "SwiftCell")
        self.view.addSubview(self.tableView!)
        
        //创建NSURL对象
        let urlString = "http://www.hangge.com/getData.php"
        let url = URL(string:urlString)!
        //创建请求对象
        let request = URLRequest(url: url)
        let session = URLSession.shared
        
        let dataTask = session.dataTask(with: request,
               completionHandler: {(data, response, error) -> Void in
                if error != nil{
                    print(error?.localizedDescription)
                }else{
                    self.ctrlnames = try! JSONSerialization.jsonObject(with: data!,
                        options: JSONSerialization.ReadingOptions.mutableContainers)
                        as! [AnyObject]
                    
                    DispatchQueue.main.async{
                        self.tableView?.reloadData()
                    }
                }
        })
        
        //使用resume方法启动任务
        dataTask.resume()
    }
    
    //在本例中,只有一个分区
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1;
    }
    
    //返回表格行数(也就是返回控件数)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.ctrlnames.count
    }
    
    //创建各单元显示内容(创建参数indexPath指定的单元)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //为了提供表格显示性能,已创建完成的单元需重复使用
        let identify:String = "SwiftCell"
        //同一形式的单元格重复使用,在声明时已注册
        let cell = tableView.dequeueReusableCell(withIdentifier: identify,
                                                 for: indexPath) as UITableViewCell
        cell.accessoryType = .disclosureIndicator
        let item = self.ctrlnames[indexPath.row]
        cell.textLabel?.text = item.object(forKey: "name") as? String
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
评论6
  • 6楼
    2017-06-14 16:53
    我是菜鸟

    航哥!!很激动,纠结了我一天的问题终于在看到你这个帖子后,解决了,很棒,很好,很好,很好。会经常关顾的!!!!

    站长回复

    谢谢你的支持,欢迎常来看看。

  • 5楼
    2017-02-03 15:45
    LeoShen

    站长,如果要跳转怎么写

    站长回复

    你指的是点击单元格跳转到新页面吗?可以参考我的这篇文章:Swift - 纯代码实现页面segue跳转,以及参数传递

  • 4楼
    2016-10-19 09:16
    小学生

    dispatch_async(dispatch_get_main_queue(), {
    self.tableView?.reloadData()
    return
    })
    航哥:有return和没return有什么区别呢?

    站长回复

    没区别的,我现在给去了,顺便把代码升级成Swift3。

  • 3楼
    2016-09-12 10:43
    swift学习者

    很喜欢这个网站,swift 很详细,学习当中,很赞

    站长回复

    很高兴你能喜欢,欢迎常来看看,我会持续更新的。

  • 2楼
    2016-04-06 21:39
    abc

    不放在主线程,运行中会有错误日志的吧

    站长回复

    不会有错误啊,而且self.tableView?.reloadData()是放在主线程执行的。

  • 1楼
    2016-04-04 23:38
    Gavin

    航哥,如果这个cell里还有一个imageView,json中还有远程图片url,如果这样在cellForRowAtIndexPath方法中,赋值的话,会很卡,这个该怎么优化呢

    站长回复

    是不是图片尺寸太大,或者图片太多。可以考虑开始只加载当前页面上的图片,可以参考这篇文章:Swift - 表格图片加载优化(拖动表格时不加载,停止时只加载当前页图片)
    地址:http://www.hangge.com/blog/cache/detail_890.html