1

I have UITableView with images in each cell and I want my scrolling be smooth. So I read some post on stackerflow and now I am loading my images in background thread:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell: BuildingStatusCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuildingStatusCell
    cell.selectionStyle = UITableViewCellSelectionStyle.None
    var node = nodesArray[indexPath.row] as! NSMutableDictionary
    if !checkIfImagesLoaded(node[Api.pictures] as! NSMutableArray) {
        cell.id = node[Api.buildingStatusId] as! Int
        cell.date.text =  node[Api.date] as? String
        cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
        cell.indicator.hidesWhenStopped = true
        cell.indicator.startAnimating()
        dbHelper.getBuildingStatusNode(node, callback: self)
    } else {
        cell.id = node[Api.buildingStatusId] as! Int
        cell.date.text =  node[Api.date] as? String
        cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
        dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
            var image = WorkWithImage.loadImageFromSD((node[Api.pictures] as! NSMutableArray)[0]["image"] as! String)! // Bad
            dispatch_async(dispatch_get_main_queue()) {
                cell.imgView.image = image
                cell.indicator.stopAnimating()
            }
        }
    }
    return cell
}

dbHelper.getBuildingStatusNode(node, callback: self) method executes in background thread also. But for some reasons when I scroll I still get some delay. I read that it is good to fill my cell with data in tableView:willDisplayCell method instead tableView:cellForRowAtIndexPath and I should return cell as faster as I can in tableView:cellForRowAtIndexPath method. The question is should I now use the code like this:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell: BuildingStatusCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuildingStatusCell
    cell.selectionStyle = UITableViewCellSelectionStyle.None
    return cell
}

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    var cell: BuildingStatusCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuildingStatusCell
    var node = nodesArray[indexPath.row] as! NSMutableDictionary
    if !checkIfImagesLoaded(node[Api.pictures] as! NSMutableArray) {
        cell.id = node[Api.buildingStatusId] as! Int
        cell.date.text =  node[Api.date] as? String
        cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
        cell.indicator.hidesWhenStopped = true
        cell.indicator.startAnimating()
        dbHelper.getBuildingStatusNode(node, callback: self)
    } else {
        cell.id = node[Api.buildingStatusId] as! Int
        cell.date.text =  node[Api.date] as? String
        cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
        dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
            var image = WorkWithImage.loadImageFromSD((node[Api.pictures] as! NSMutableArray)[0]["image"] as! String)!
            dispatch_async(dispatch_get_main_queue()) {
                cell.imgView.image = image
                cell.indicator.stopAnimating()
            }
        }
    }
}

And what else I can do to make my scrolling more smooth? BCS I still have lags even when I use willDisplayCell method.

P.S. Image size in my UITableViewCells is fixed.

Community
  • 1
  • 1
Panich Maxim
  • 1,105
  • 1
  • 15
  • 34
  • I would suggest subclasses UITableViewCell and have a cell.configureWithDictionary(cellDictionry) control all that information to move it away from the view controller, after that, are you stopping/continue downloads of images ? when images move off screen you no longer need to "download" them queueing up a lot of downloads could slow down the app, how many images are you loading as well? you could be using up a lot of memory. – A'sa Dickens Sep 03 '15 at 11:36
  • 1
    I don't know your exact setup so i can't be certain of specifically how to make it more smoothly, but there's been a couple different wenderlich tutorials on concurrent calls using NSURLSession, NSOperation and other useful classes with in Apple's apis if you have the time i would read those. – A'sa Dickens Sep 03 '15 at 11:38
  • 1
    Alos, don't use AFNetworking or SDWebImage, they are nice to use when you actually know what is happening in the background, but if you do not then you are just poking holes in a condom and still having sex it gets the job done for the most part, but when something bad happens you can't control it. – A'sa Dickens Sep 03 '15 at 11:40
  • Here's a quick and easy answer, subclass UITableView Cell for your custom implementation and then do this for the image downloading thing: http://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift – A'sa Dickens Sep 03 '15 at 11:43
  • Thx for answer. I have cashing mechanism to get images and it works in background thread, so if image is new or it has been changed I download it and save localy and then retrieve it when I need to show a cell. Anyway, lags are the same when app is downloading images and when they are all saved on phone. – Panich Maxim Sep 03 '15 at 11:52
  • Is the whole image retrieval process on the background thread ? or just the downloading. In other wards only return to the main thread when you go to imageView.image = retrievedImage; – A'sa Dickens Sep 03 '15 at 11:57
  • Should I use cell.configureWithDictionary(cellDictionry) method in tableView:willDisplayCell method? – Panich Maxim Sep 03 '15 at 11:58
  • Yeap, I moved image retrieval process in background thread. – Panich Maxim Sep 03 '15 at 11:59
  • i've never used tableView:willDisplayCell method, but i don't see why that wouldn't work, i just configure the cell when i create the cell. Frankly the cell.configureWithDictionary thing is just good architecture it shouldn't speed up your tableview scroll speed – A'sa Dickens Sep 03 '15 at 12:00
  • Try commenting out different lines that set data in the cell and see if it speeds up any, then try not getting any images at all not calling the methods to download or anything. This will help isolate where the lag is coming from. – A'sa Dickens Sep 03 '15 at 12:02
  • Agreed with @A'saDickens, do not use AFNetworking or SDWebImage... even if you know what you're doing.. libraries like this should be used sparingly. Too much unnecessary dependencies and overhead for no real benefit. – TheCodingArt Sep 03 '15 at 12:50

3 Answers3

6

Try the following

  • Try removing any shadows.
  • Make the cell and its subviews opaque. Don't use alpha/transparency.
  • Try decoding the images on a background thread : Decode images in background thread?
Community
  • 1
  • 1
Samhan Salahuddin
  • 2,140
  • 1
  • 17
  • 24
1

First of all it is better to subclass UITableViewCell and just pass your Api object to cell and make this mapping inside cell.

Also it is better to use some library like: AFNetworking's extension or AsyncImageView - it is possible to use in Swift.

Try to remove any border rounding, shadow, transparencies - they can cause delays. In this case you need rasterization:

Related question: Sluggish scrolling experience when using QuartzCore to round corners on UIImageView's within a UITableViewCell

Community
  • 1
  • 1
AlexZd
  • 2,152
  • 2
  • 18
  • 29
  • Just to note, no it is not better to use some library for something that's extremely easy to do yourself. Please do not recommend for people who do not understand how to do things to default to a library.. especially when those things are relatively simple tasks..... – TheCodingArt Sep 03 '15 at 18:31
-3

When you load image from URL it takes time to download image and that cause block in scrolling UITableView. You are doing so much work simply do

  1. Use this class SDWebImage

  2. and in your bridging header file :

    #import "UIImageView+WebCache.h"

  3. Here is a code example that should work :

    let block: SDWebImageCompletionBlock! = {(image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, imageURL: NSURL!) -> Void in
        println(self)
    }
    
    let url = NSURL(string: node[Api.pictures] as! NSMutableArray)[0]["image"] as! String)
    
    cell.imgView.sd_setImageWithURL(url, completed: block)
    
Kampai
  • 22,848
  • 21
  • 95
  • 95
baydi
  • 1,003
  • 6
  • 11