简体   繁体   中英

Swift: How to refresh tableview without lag (UIRefreshControl)

Filling my table view with objects from a MYSQL database using PHP and JSON to Swift 3. I have a pull down to refresh function but when I'm pulling down to refresh it lags mid way for a second and then continues (like the wheel won't spin for a second).

How can I update my tableview smoother because I'm guessing as I add more content to the database in the future the bigger the lag. I currently have 12 objects in my database so imagine with 100+ objects.

In 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")
    }

Then pull to refresh function

// 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()
}

Refer to the apple sample code at the following location:

http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html

Couple of suggestion :

Don't show the data at cellForRowAtIndexPath: method 'cause at this time cell is not displayed yet. Try to use tableView:willDisplayCell:forRowAtIndexPath: method in the delegate of UITableView.

Re-Use single instance of cell/header/footer even if you need to show more.

Let me know if anything specific is needed.

    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
        }
    }

My guess is that your retrieveDataFromServer() is blocking the main thread, and therefore causing the lag. Try wrapping it in an async block

// 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?()
}

I imagine the lag you're experiencing is due to the network request being executed synchronously on the main thread:

let data: Data = try Data(contentsOf: url as URL)

Network requests are slow and should almost certainly be done off the main thread. The solution here is to move the networking call to a background thread so the main (UI) thread doesn't get blocked (lag).

So how do you do that? Well that is a large question with many different answers.

I highly recommend you spend some time learning about multi-threaded programming (also known as concurrency) in Swift. Going through this Ray Wenderlich tutorial should give you a good foundation.

Then it's probably a good idea to learn about URLSession which is used for performing asynchronous network requests in iOS apps. Again Ray Wenderlich has a great starter tutorial .

Finally... here is a quick and dirty solution for you. It's "hacky" and you probably shouldn't use it, but it will probably fix your lag issue:

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()
            }
        }
    }
}

Try this:

@objc func handleRefresh(_ refreshControl: UIRefreshControl) {

    refreshControl.endRefreshing()
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {

    retrieveDataFromServer()
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM