Swift - 异步加载表格数据,内容不能及时显示的问题解决
作者:hangge | 2016-02-22 09:38
(本文代码已升级至Swift3)
(2)使用 URLSession 获取远程数据后,调用 tableView的reloadData() 方法重新加载数据。
(3)但会发现数据加载完毕后表格还是空白的,拖动一点点表格数据就显示出来了。

2,解决办法
reloadData() 方法需要在主线程中调用,这样表格数据就能及时更新。(代码高亮出为修改的地方)
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)
航哥!!很激动,纠结了我一天的问题终于在看到你这个帖子后,解决了,很棒,很好,很好,很好。会经常关顾的!!!!
站长回复:谢谢你的支持,欢迎常来看看。
站长,如果要跳转怎么写
站长回复:你指的是点击单元格跳转到新页面吗?可以参考我的这篇文章:Swift - 纯代码实现页面segue跳转,以及参数传递
dispatch_async(dispatch_get_main_queue(), {
self.tableView?.reloadData()
return
})
航哥:有return和没return有什么区别呢?
站长回复:没区别的,我现在给去了,顺便把代码升级成Swift3。
很喜欢这个网站,swift 很详细,学习当中,很赞
站长回复:很高兴你能喜欢,欢迎常来看看,我会持续更新的。
不放在主线程,运行中会有错误日志的吧
站长回复:不会有错误啊,而且self.tableView?.reloadData()是放在主线程执行的。
航哥,如果这个cell里还有一个imageView,json中还有远程图片url,如果这样在cellForRowAtIndexPath方法中,赋值的话,会很卡,这个该怎么优化呢
站长回复:是不是图片尺寸太大,或者图片太多。可以考虑开始只加载当前页面上的图片,可以参考这篇文章:Swift - 表格图片加载优化(拖动表格时不加载,停止时只加载当前页图片)
地址:http://www.hangge.com/blog/cache/detail_890.html