JFed-9
JFed-9

Reputation: 297

UITableView lags when scrolling

I realize that there are several similar questions to this one, and I have indeed checked a lot of them. However, the ones I checked were either in the wrong language, or not what I was looking for, therefore I'm asking a new question. For reference, I'm using XCode 7.3.1 in Swift 2.

There is one feature of my new iOS app that will search the internet for data, and display the results by populating a UITableView. My problem is that it displays pictures in each cell, which makes the scrolling very annoying.

My ideal solution would be to have a default picture display, and while allowing the user to continue scrolling, it will download in the background. Then, once it has retrieved it, it will replace the default. I saw some previous answers on how to do this, but none have worked for me.

P.S. If you see another question that solves this problem WITH SWIFT, post the url and I'll delete this one.

Here is the code I'm using currently (with the lag):

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  // Creates each cell, by parsing through the data received from the Employees array which we returned from the database
    if (Employees.count == 0)
    {
        let cell = self.ResultsTable.dequeueReusableCellWithIdentifier("NORESULT", forIndexPath: indexPath) as! TruckSearchNRTableViewCell

        return cell
    }

    else {
        let cell = self.ResultsTable.dequeueReusableCellWithIdentifier("RESULT", forIndexPath: indexPath) as! TruckSearchTableViewCell

            cell.companyLabel.text = Employees[indexPath.row]["CompanyName"] as? String
            cell.nameLabel.text = Employees[indexPath.row]["EmployeeName"] as? String
        if let mobile = Employees[indexPath.row]["PhoneNumber"] as? String {
            if (mobile == "")
            {
                cell.mobileLabel.hidden = true
                cell.mobileTitle.hidden = true
            } else {
                cell.mobileTitle.hidden = false
                cell.mobileLabel.hidden = false
            }
            let phonenumber = mobile.stringByReplacingOccurrencesOfString("[^0-9]", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range:nil);
            cell.phoneNumber.text = phonenumber
        }
            cell.titleLabel.text = Employees[indexPath.row]["JobTitle"] as? String
            cell.truckLabel.text = Employees[indexPath.row]["TruckNumber"] as? String
            cell.supervisorLabel.text = Employees[indexPath.row]["SupervisorName"] as? String
        if (Employees[indexPath.row]["Synced"] as? Int) == 0 {
            turnRed(cell)
        } else {
            turnBlack(cell)
        }
        if let ePhoto = Employees[indexPath.row]["PicLocation"] as? String {  // Save complete URL of picture location, and save it to the table

            let url = NSURL(string: "https://exampleurl.com/\(ePhoto)")!
            if let data = NSData(contentsOfURL: url){
                let myImage = UIImage(data: data)
                cell.employeePhoto.image = myImage
            }
            else
            {
                cell.employeePhoto.image = UIImage(named: "person-generic")
            }

        }

        return cell
    }
}

Upvotes: 1

Views: 4210

Answers (1)

Olivier Wilkinson
Olivier Wilkinson

Reputation: 2846

The problem is that you are retrieving the picture on the main thread, which is blocking it. All UI operations, including scrolling, are performed on the main thread so that is why it is choppy at the moment.

To solve this, instead of:

if let data = NSData(contentsOfURL: url){

    let myImage = UIImage(data: data)
    cell.employeePhoto.image = myImage

} else {

    cell.employeePhoto.image = UIImage(named: "person-generic")

}

Put the image request on a background thread and then set the image on the main thread again.

Eg

let qos = QOS_CLASS_USER_INITIATED

dispatch_async(dispatch_get_global_queue(qos,  0), {

   if let data = NSData(contentsOfURL: url){

       let myImage = UIImage(data: data)

       dispatch_async(dispatch_get_main_queue(), {
           cell.employeePhoto.image = myImage
       })

   } else {

       dispatch_async(dispatch_get_main_queue(), {
           cell.employeePhoto.image = UIImage(named: "person-generic")
       })

   }

})

alternatively you can use an asynchronous url session to get the data like so:

let request: NSURLRequest = NSURLRequest(URL: url)
let mainQueue = NSOperationQueue.mainQueue()

NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in

    if error == nil {

        let myImage = UIImage(data: data)

        dispatch_async(dispatch_get_main_queue(), {
            cell.employeePhoto.image = myImage  
        })

    } else {

       dispatch_async(dispatch_get_main_queue(), {
           cell.employeePhoto.image = UIImage(named: "person-generic")
       })

    }

})

Upvotes: 6

Related Questions