John Doe
John Doe

Reputation: 821

Why collectionView freezes on scroll?

I'm trying to create a simple app with photo filters, like Instagram. So, I capture my image then I need to implement filter to this image. On the bottom I have a UICollectionView(horizontal scroll) where I can see what filters I have and how they will look on my image.

But when I scroll my UICollectionView - it freezes and my filters applying on every new cell(because of reusing process). But how Instagram does it that when I scroll the filters they does not freeze?

I've tried this:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: imageFilterCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! imageFilterCell

let queue = NSOperationQueue()

    let op1 = NSBlockOperation { () -> Void in
        let img = UIImage(data: UIImageJPEGRepresentation(self.image!, 0.1)!)
        let img1 = self.applyFilterTo(img!, filter: self.filtersImages[indexPath.row])

        NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
            cell.imageView.image = img1
        })
    }

    queue.addOperation(op1);

    return cell
}

but it still freezes on scroll and I see how every time my filters are applying to my cells. Can I do it just one time and then on scroll to do nothing, just showing how the photo will looks after implementing the filter?

Upvotes: 1

Views: 3136

Answers (3)

Programer_saeed
Programer_saeed

Reputation: 133

you can cache data (Images) in memory its problem have a lovely relation with read data(images) :D

Upvotes: 0

Jelly
Jelly

Reputation: 4522

You could do one of the two things:

  1. Do not use the reuse feature of the collection view. Simply create an array of cells and pass those cells to cellForRowAtIndexPath. This should work fine if you don't have a large number of filters. Something like:

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        var cell: imageFilterCell? = nil
        if (cellsArray.count <= indexPath.row) {
            //This assumes imageFilterCell is created in code
            //If you use storyboards or xibs load it accordingly
            cell = imageFilterCell()
            cellsArray.append(cell)
        } else {
            cell = cellsArray[indexPath.row]
        }
        let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
        dispatch_async(dispatch_get_global_queue(priority, 0)) {
           let img = UIImage(data: UIImageJPEGRepresentation(self.image!, 0.1)!)
           let img1 = self.applyFilterTo(img!, filter: self.filtersImages[indexPath.row])
    
           dispatch_async(dispatch_get_main_queue()) {
                 cell!.imageView.image = img1
           }
    }
    
        return cell
    

    }

  2. Implement a cancel mechanism for the async operation, because if the cell is reused while the background thread is still applying the filter the final cell will end up having multiple filters. For this you could use the cancel method from the NSOperation class. For this you could create a property on your custom cell called operation something like:

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell: imageFilterCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! imageFilterCell
        cell.operation.cancel()    
    
        //make the queue an instance variable
        //let queue = NSOperationQueue()
    
        let op1 = NSBlockOperation { () -> Void in
            let img = UIImage(data: UIImageJPEGRepresentation(self.image!, 0.1)!)
            let img1 = self.applyFilterTo(img!, filter: self.filtersImages[indexPath.row])
    
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                cell.imageView.image = img1
            })
        }
    
        queue.addOperation(op1);
        cell.operation = op1
    
        return cell
    

    }

Upvotes: 3

Ulysses
Ulysses

Reputation: 2317

You are processing in the main thread, this is why it freezes.

Use a Concurrent Dispatch Queue for a more fluid experience.

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    let cell: imageFilterCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! imageFilterCell


   let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
   dispatch_async(dispatch_get_global_queue(priority, 0)) {
           let img = UIImage(data: UIImageJPEGRepresentation(self.image!, 0.1)!)
           let img1 = self.applyFilterTo(img!, filter: self.filtersImages[indexPath.row])

           dispatch_async(dispatch_get_main_queue()) {
                 cell.imageView.image = img1
           }
    }

        return cell
}

Upvotes: 0

Related Questions