Search code examples
iosswiftasynchronousreloaddata

How to load view only after query callback


I have a collectionView of photos as a subview of another view. I would like to display it only after I have performed a query to get the pictures to populate it, and put an activity indicator if it is not ready. I tried to put my code in viewWillAppear, but I don't get the photo downloaded before displaying the view. What would be the best way to handle this?

override func viewWillAppear(animated: Bool) {

    super.viewWillAppear(animated)

    let query = queryAPI()
    query.userExtra(currentUser, completion: { (user:ModelUser) -> () in

        self.currentUser = user

        if let userPhotos = self.currentUser.photos as [ModelAttachment]! {

            self.photos = [UIImage]()

            for object in userPhotos as [ModelAttachment] {

                Utils.getImageAsync(object.url!, completion: { (img: UIImage?) -> () in

                    self.photos?.append(img!)
                    object.image = img

                })

            }

            self.photosCollectionView.reloadData()

        }

    })


}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if collectionView == photosCollectionView {

        if let hasPhotos = photos as [UIImage]! {

            return photos!.count

        } else {

            return 0
        }

    }else{
        return friends.count
    }
}

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

    if collectionView == photosCollectionView {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UICollectionViewCell

        let imageView = cell.viewWithTag(1) as! UIImageView
        let photo = photos![indexPath.row]
        imageView.image = photo
        return cell

    }
}

And the function to get the images async:

class func getImageAsync(url:String, completion: (UIImage?) -> ()){
    var escapedUrl = url.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
    var imageUrl = NSURL(string: escapedUrl!)

    if(imageUrl == nil)
    {
        println("Invalid Url!")
        completion(nil)
    }
    else
    {
        var stdUrl = imageUrl!.standardizedURL!
        println("Downlading image: "+stdUrl.description)
        let req = NSURLRequest(URL: stdUrl)
        NSURLConnection.sendAsynchronousRequest(req, queue: NSOperationQueue.mainQueue()) { (resp, data, error) -> Void in
            var image: UIImage?
            if data != nil {
                image = UIImage(data: data!)!
            } else {
                println("Error while download the image: \(error)")

            }

            dispatch_async(dispatch_get_main_queue()) {
                completion(image)
            }

        }
    }

}

Solution

  • In response to your comment, "The function is in another file as I am using it in different places in my code, how can I call reload the data of another controller there?"

    There are multiple ways to do this. One method is to post a NSNotification and add an observer on your view controller.

    To post a notification:

    [[NSNotificationCenter defaultCenter] 
            postNotificationName:@"TestNotification" 
            object:self];
    

    To receive and respond to a notification:

    - (void) viewDidLoad {
        [[NSNotificationCenter defaultCenter] addObserver:self
                selector:@selector(receiveTestNotification:) 
                name:@"TestNotification"
                object:nil];
    }
    
    - (void) receiveTestNotification:(NSNotification *) notification
    {
        if ([[notification name] isEqualToString:@"TestNotification"])
            [self.photosCollectionView reloadData];
    }