1

I have a collectionView cell which has five imageviews. I am using the following the answer given by javimuu to load images in the background. Link

It works well and all the images load in the correct imageview. But it is very slow process and the images load in random order. I am having difficulty in showing the activity indicator and hiding it when all the images are done loading for a specific cell i.e, Keep showing indicator while images are loading and hide it when all 5 images are loaded.

Please help.

Update:

    func downloadImageFromLink(link:NSURL,group: dispatch_group_t, completion:(isDone: Bool) -> Void) {

    self.image = nil

    imageURL = link.absoluteString
    dispatch_group_enter(group)

    NSURLSession.sharedSession().dataTaskWithURL(link, completionHandler: {
        (data, response, error) -> Void in
        if error != nil {
            print(error)
            return
        }

        if let imageFromCache = imageCache.objectForKey(link.absoluteString) as? UIImage {

            self.image = imageFromCache
            dispatch_group_leave(group)
            return
        }
        dispatch_async(dispatch_get_main_queue(),{

            let imageToCache =  UIImage(data: data!)

            if self.imageURL == link.absoluteString {
                self.image = imageToCache
                imageCache.setObject(imageToCache!, forKey: link.absoluteString)
            }
            dispatch_group_leave(group)
        })
    }).resume()
}

func loadImagesInCell(cell: WatchFaceCell, images:NSArray ,completion:() ->Void) {
    self.startAnimating(cell.contentView)
    let group: dispatch_group_t = dispatch_group_create()
    for model in images{
        if model is WFCreatorModel {
            let wfModel: WFCreatorModel = model as! WFCreatorModel
            switch wfModel.imageType {
            case .Dial:
                cell.dialImage.downloadImageFromLink(wfModel.imageURL,group: group, completion: { (isDone) in
                })
            case .HourHand:
                cell.hourHandImage.downloadImageFromLink(wfModel.imageURL,group: group, completion: { (isDone) in
                })
            case .MinuteHand:
                cell.minuteHandImage.downloadImageFromLink(wfModel.imageURL,group: group, completion: { (isDone) in
                })
            case .SecondHand:
                cell.secondHandImage.downloadImageFromLink(wfModel.imageURL,group: group, completion: { (isDone) in
                })
            case .Notification:
                cell.notificationImage.downloadImageFromLink(wfModel.imageURL, group: group,completion: { (isDone) in
                })
            }//end of switch
        }//end of if else
    }//end of for
    dispatch_group_notify(group, dispatch_get_main_queue()) {
        self.stopAnimating(cell.contentView)
        completion()
    }
}
Community
  • 1
  • 1
Amogh Shettigar
  • 275
  • 2
  • 3
  • 18
  • The slowness might be due to images being large. A quickie solution for knowing when all are done is to keep a counter: increment it before the dispatch async and decrement it (and check for zero) in the dispatch on the main queue. – danh Mar 19 '17 at 21:14
  • @danh can you please explain with an example? – Amogh Shettigar Mar 20 '17 at 07:16

2 Answers2

1

You're going to need to use a DispatchGroup which you can enter and leave when you make asynchronous requests. You then use the .notify(queue:) method which fires when the group is 'empty'. I was able to find an answer with code very similar to what you're looking for so I've linked it here: Wait until swift for loop with asynchronous network requests finishes executing.

Let me know if you have any more problems. Hope this helps!

Community
  • 1
  • 1
CoolPenguin
  • 1,215
  • 12
  • 21
  • Thanks for your reply. I have tried the dispatchGroup and it suits my needs but I am still facing an issue where the activity indicator will quickly show up and vanish. It hits the dispatch_group_leave(group) where I am maintaining my cache 5 times and then hits the dispatch_group_notify call. But the images are not loaded in the imageView at that point. Calling them in the main_queue() causes the UI to lag, which was my original problem and hence all this code would be of no use. – Amogh Shettigar Mar 20 '17 at 07:18
  • What do you mean when you say the images are not loaded in the imageView at that point? They're not loaded when the notify function runs? – CoolPenguin Mar 20 '17 at 10:09
  • I mean to say I am assigning them an image after I get the result from NSURLSession, but since they are not running on the main thread they are not assigned immediately. If I run it on the main thread then the UI lags. – Amogh Shettigar Mar 20 '17 at 10:16
  • so you assign the images to their imageViews all at once in the dispatch_group_notify(group, dispatch_get_main_queue()) { – CoolPenguin Mar 20 '17 at 10:39
  • I will give that a try and let you know. Thanks! – Amogh Shettigar Mar 20 '17 at 12:34
  • Thanks it worked. Still a bit of lag remains when applying the image, but I guess that will always be there. – Amogh Shettigar Mar 21 '17 at 06:43
  • Ok, glad I could help! – CoolPenguin Mar 21 '17 at 07:32
0

You should try using a third party like kingfisher which will have all the necessary methods for performing such tasks.. Here is some sample code below just to give you something...

var prefetcher: ImagePrefetcher?
var imageItems: [ImageItem]? {
    didSet {
        collectionView.reloadData()
    }
}

// This code goes inside one of your methods
                      self.prefetcher = ImagePrefetcher(urls: urls, optionsInfo: nil, progressBlock: nil, completionHandler: { [weak self]
                            (skippedResources, failedResources, completedResources) -> () in
                            self?.endLoading()
                            self?.imageItems = filteredItems
                            self?.timerLabel.countFrom(15, to: 0, withDuration: 15.0)
                            self?.timerLabel.completionBlock = { self?.startGuessing() }
                        })
        self.prefetcher!.start()
Ankit Srivastava
  • 12,347
  • 11
  • 63
  • 115
  • Thanks for the reply, but this does not solve my problem. How will I know that for a particular cell all 5 images have loaded? The example you have given is only for cached images and not for downloading images. I need to download each image separately so that I can set it for the correct imageView in the correct cell. – Amogh Shettigar Mar 20 '17 at 06:30