Swift:如何无延迟地刷新 tableview (UIRefreshControl)
Posted
技术标签:
【中文标题】Swift:如何无延迟地刷新 tableview (UIRefreshControl)【英文标题】:Swift: How to refresh tableview without lag (UIRefreshControl) 【发布时间】:2017-07-06 03:28:44 【问题描述】:使用 php 和 JSON 到 Swift 3 用 mysql 数据库中的对象填充我的表视图。我有一个下拉刷新功能,但是当我下拉刷新时它会在中途延迟一秒钟然后继续(比如***不会转动一秒钟)。
我怎样才能更流畅地更新我的 tableview,因为我猜测将来我向数据库添加更多内容时,滞后越大。我的数据库中目前有 12 个对象,所以想象一下有 100 多个对象。
在 viewDidLoad 中
// Pull to Refresh
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
if #available(ios 10.0, *)
myTableView.refreshControl = refreshControl
print("iOS 10")
else
myTableView.addSubview(refreshControl)
print("iOS 9 or iOS 8")
然后拉动刷新功能
// Pull to Refresh
func handleRefresh(refreshControl: UIRefreshControl)
// Fetching Data for TableView
retrieveDataFromServer()
// Stop Refreshing
refreshControl.endRefreshing()
// Retrieving Data from Server
func retrieveDataFromServer()
// Loading Data from File Manager
loadData()
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
do
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Clear the arrays
self.followedArray = [Blog]()
self.mainArray = [Blog]()
// Looping through jsonArray
for jsonObject in jsonArray
if let blog = Blog(jsonObject:jsonObject as! [String : Any])
// Check if Identifiers Match
if followedIdentifiers.contains(blog.blogID)
self.followedArray.append(blog)
else
self.mainArray.append(blog)
catch
print("Error: (Retrieving Data)")
myTableView.reloadData()
【问题讨论】:
您在retrieveDataFromServer() 中调用loaddata() 尝试在后台队列中调用loadData() 和Api 机制。出现延迟是因为加载 loadData() 和 Api 机制的所有参数需要几秒钟时间 @iOSGeek 即使我评论//loadData()
它仍然滞后
不要评论只使用异步机制并尝试使用,因为对我来说这总是有效我应该评论基本异步机制吗?
您正在从主线程中的服务器同步获取。这就是它滞后的原因。尝试遵循一些教程并异步获取它。
好的,但是使用 async 方法异步获取数据,所以没有延迟
【参考方案1】:
参考以下位置的苹果示例代码:
http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html
几个建议:
不显示 cellForRowAtIndexPath 处的数据:方法'因为此时单元格尚未显示。尝试在 UITableView 的委托中使用 tableView:willDisplayCell:forRowAtIndexPath: 方法。
即使您需要显示更多内容,也可以重复使用单元格/页眉/页脚的单个实例。
如果需要任何具体内容,请告诉我。
【讨论】:
【参考方案2】: Spinner.isHidden = false
Spinner.startAnimating()
DispatchQueue.global(qos: .background).async
loadData()
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
do
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Clear the arrays
self.followedArray = [Blog]()
self.mainArray = [Blog]()
// Looping through jsonArray
for jsonObject in jsonArray
if let blog = Blog(jsonObject:jsonObject as! [String : Any])
// Check if Identifiers Match
if followedIdentifiers.contains(blog.blogID)
self.followedArray.append(blog)
else
self.mainArray.append(blog)
catch
print("Error: (Retrieving Data)")
DispatchQueue.main.async
myTableView.reloadData()
self.Spinner.startAnimating()
self.Spinner.isHidden = true
【讨论】:
我的api机制应该是retrieveDataFromServer()
code?span>
给我一个崩溃说This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
对不起,我在后台队列中重新加载了 tableView,这里只是在主队列中重新加载 tableView 意味着只需在“DispatchQueue.main.async”上方剪下这一行“myTableView.reloadData()”并写入“myTableView.reloadData() " 就在 "self.Spinner.startAnimating()" 之前
欢迎随时寻求帮助
最后一次 startanimating() 调用应该是 stopanimating()。【参考方案3】:
我的猜测是您的 retrieveDataFromServer() 阻塞了主线程,因此导致了延迟。尝试将其包装在异步块中
// Pull to Refresh
func handleRefresh(refreshControl: UIRefreshControl)
// Fetching Data for TableView
retrieveDataFromServer [weak refreshControl] in
// This block will run once retrieveDataFromServer() is completed
// Reload data
myTableView.reloadData()
// Stop Refreshing
refreshControl?.endRefreshing()
// Retrieving Data from Server
func retrieveDataFromServer(completion: (() -> Void)?)
// Loading Data from File Manager
loadData()
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
do
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Clear the arrays
self.followedArray = [Blog]()
self.mainArray = [Blog]()
// Looping through jsonArray
for jsonObject in jsonArray
if let blog = Blog(jsonObject:jsonObject as! [String : Any])
// Check if Identifiers Match
if followedIdentifiers.contains(blog.blogID)
self.followedArray.append(blog)
else
self.mainArray.append(blog)
catch
print("Error: (Retrieving Data)")
// Calls completion block when finished
completion?()
【讨论】:
【参考方案4】:我想您遇到的延迟是由于网络请求在主线程上同步执行:
let data: Data = try Data(contentsOf: url as URL)
网络请求很慢,几乎可以肯定是在主线程之外完成的。这里的解决方案是将网络调用移动到后台线程,这样主(UI)线程就不会被阻塞(滞后)。
那么你是怎么做到的呢?嗯,这是一个很大的问题,有许多不同的答案。
我强烈建议您花一些时间学习 Swift 中的多线程编程(也称为并发)。通过这个Ray Wenderlich tutorial 应该会给你一个很好的基础。
那么,了解用于在 iOS 应用程序中执行异步网络请求的 URLSession 可能是个好主意。再次,Ray Wenderlich 有一个很棒的starter tutorial。
最后......这是一个快速而肮脏的解决方案。它很“hacky”,您可能不应该使用它,但它可能会解决您的延迟问题:
func retrieveDataFromServer()
// Loading Data from File Manager
loadData()
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
// Move to a background queue to fetch and process data from network.
DispatchQueue.global().async
// Don't touch anything related to the UI here.
do
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Create new temp arrays to process json
var tempFollowedArray = [Blog]()
var tempMainArray = [Blog]()
// Looping through jsonArray
for jsonObject in jsonArray
if let blog = Blog(jsonObject:jsonObject as! [String : Any])
// Check if Identifiers Match
if self.followedIdentifiers.contains(blog.blogID)
tempFollowedArray.append(blog)
else
tempMainArray.append(blog)
// Jump back to main (UI) thread to update results
DispatchQueue.main.async
print("success")
self.followedArray = tempFollowedArray
self.mainArray = tempMainArray
self.myTableView.reloadData()
catch
DispatchQueue.main.async
print("Error: (Retrieving Data)")
// This reload is probably not necessary, but it was
// in your original code so I included it.
self.myTableView.reloadData()
【讨论】:
以上是关于Swift:如何无延迟地刷新 tableview (UIRefreshControl)的主要内容,如果未能解决你的问题,请参考以下文章
如何初始加载 UITableView 然后观察 Firebase 节点以更改在 Swift 中刷新 tableview?
Swift - RxSwift的使用详解32(UITableView的使用3:刷新表格数据)