Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView is Lagging when Displaying Images Swift

I have a tableView that shows images in the cells, and I'm fetching the data in viewDidLoad function

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
                let cell = tableview.dequeueReusableCellWithIdentifier("cardSelectionCell", forIndexPath: indexPath) as! MyCell
                let card = fetchedResultsController.objectAtIndexPath(indexPath) as! Card
                cell.Name?.text = card.name
                var image: NSData = card.photo as NSData
                cell.logo.image = UIImage(data: image)
                let date = NSDate()
                let formatter = NSDateFormatter()
                formatter.timeStyle = .MediumStyle
                cell.policyExpiryDate.text = formatter.stringFromDate(date)
                return cell 
        }

But the problem is that when I start scrolling, the tableView is very laggy so i tried to create a dictionary to convert the images in viewDidLoad()

var imageDictionary = [Card:UIImage]()


AllCards = context.executeFetchRequest(request, error: nil) as! [Card]
        for card in AllCards {
            imageDictionary[card] = UIImage(data: card.photo as NSData)
        }

and in the tableView function:

cell.logo.image = imageDictionary[card]

but it still lagging

one more question: if one card is added to the core data, i have to fetch the Array of images again, so i tried to create the array in viewDidAppear() but the images does not appear in the first load and the tableView is still lagging.

like image 746
Maha Avatar asked Jan 08 '23 03:01

Maha


2 Answers

First of all it depends really on the size of your image:

var image: NSData = card.photo as NSData
cell.logo.image = UIImage(data: image)

This lines can be very heavy. You can easily measure it like this for example:

let time1 = CACurrentMediaTime()
var image: NSData = card.photo as NSData
cell.logo.image = UIImage(data: image)
print(CACurrentMediaTime() - time1)

If it takes much time (for example more than 8 milliseconds) than you should optimize it. You can do it it many ways, for example, you can store UIImage references in some dictionary or array and then pass on displaying in cellForIndexPath:. The other way is going to AsyncKit way - making your UI assync, load data in one thread and pass it to draw on main thread only. Now all you process are in main thread.

UPD: I think that the best approach in this case is to store pathes to images, then run in background process for UIImage resize and return preview. Also you can make cache for resized and not resized UIImage, the path or URL can be a key - so next time it will cost only to take UIImage from cache not to launch all the resize process again.

Also shoul notice that this operation:

cell.logo.image = UIImage(data: image)

create a system cache and can make big allocations in memory, because garbage collector won't kill it immediately, like in the case with UIWebView. So you can add your own autorelease pool like this around your function:

autoreleasepool { () -> () in
    cell.logo.image = UIImage(data: image)
}
like image 91
katleta3000 Avatar answered Jan 09 '23 18:01

katleta3000


I was having this issue too, I solved it by using dispatch_async. This way the table cell will load before the image finishes loading.

    let priority = DISPATCH_QUEUE_PRIORITY_HIGH

    dispatch_async(dispatch_get_global_queue(priority, 0)) {
        if let imgData = card.photo as? NSData {
            let image = UIImage(data: imageData)
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                cell.logo.image = image
            })
        }
    }
like image 43
alephao Avatar answered Jan 09 '23 16:01

alephao