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)的主要内容,如果未能解决你的问题,请参考以下文章

如何在swift中删除行后刷新tableview?

如何初始加载 UITableView 然后观察 Firebase 节点以更改在 Swift 中刷新 tableview?

Swift - RxSwift的使用详解32(UITableView的使用3:刷新表格数据)

如何在iOS swift中的表格视图按钮中进行拉动刷新[重复]

Swift 3:TableView 刷新不适用于行操作

如何实现Swift快速集成下拉刷新和上拉加载更多