1

Using JSQMessageView I don't have the image urls for all my avatars at initial load time so I have an async call that goes and fetches urls - then images for avatars.

How can I update the placeholder avatar images once I have the avatars from the async process?

I saw some notes about JSQMessageAvatarImageDataSource, but the docs show protocol methods that return just single images with no parameters for things like key or index - so not sure how to implement this protocol.

Any examples of how to implement this use-case?

My implementation thus far

  override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {

        let message = messages[indexPath.item]
        let diameter = UInt(collectionView.collectionViewLayout.incomingAvatarViewSize.width)


        if let avatar = avatars[message.senderDisplayName] {
            return avatar
        } else {

            // how do I update the avatars when i come back form the async call here

//            APIClient.instance.getUserThumbnail(Int(message.senderId)!, completion: { (url, error) -> () in
//                self.setupAvatarImage(message.senderDisplayName, imageUrl: url, diameter: 30)
//            })
            //default placeholder while the async call is happening
            return setupAvatarColor(message.senderDisplayName, diameter: diameter)


        }
    }
MonkeyBonkey
  • 46,433
  • 78
  • 254
  • 460
  • could u manage to solve it? – Delavega Jul 26 '16 at 06:51
  • Any joy with this? – David Henry Mar 18 '17 at 08:10
  • I did manage to do a workaround but I don't remember what it was exactly - I'll have to see if I can find it again... it wasn't an elegant solution but I think I returned an Image object that encapsulated the logic for updating the image in itself so that JSQ had no logic with it at all – MonkeyBonkey Mar 19 '17 at 12:14

2 Answers2

0

I used AlamofireImage cache to load and save avatars, here is set up, everything in my chat controller ChatVC: JSQMessagesViewController:

import Alamofire
import AlamofireImage

let imageCache = AutoPurgingImageCache(
    memoryCapacity: 60 * 1024 * 1024,
    preferredMemoryUsageAfterPurge: 20 * 1024 * 1024
)

var images = [UIImage]()
var ImageCache = [String:UIImage]()
var imagesURL = [String]()

override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
    let placeHolderImage = UIImage(named: "nouser")
    var avatarImage = JSQMessagesAvatarImageFactory.avatarImage(with: placeHolderImage, diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault))
    let profileImageURL = messages[indexPath.row].avatarImage

    if let avatar = ImageCache[profileImageURL] {
        avatarImage = JSQMessagesAvatarImageFactory.avatarImage(with: avatar, diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault))
    } else {
        Alamofire.request(profileImageURL)
            .responseImage { response in
                debugPrint(response.result)

                if let image = response.result.value {
                    print("image downloaded: \(image)")

                    self.ImageCache[profileImageURL] = image

                    avatarImage = JSQMessagesAvatarImageFactory.avatarImage(with: image, diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault))
                    self.finishReceivingMessage()
                } else {
                    print("error getting image at cell")
                }
        }
    }

    return avatarImage
}
Ruslan Sabirov
  • 440
  • 1
  • 4
  • 19
-1

This is my method. Note, I'm not putting the initials in for the avatars where I already know I have images coming. I've already determined which ones have no images and I've used avatarImageWithUserInitials: in a different place.

Basically, as long as the avatarImage already exists, you can pass the new image asynchronously and the avatar will automatically update the placeholder when it's ready.

UIImage *placeHolderImage = [UIImage imageNamed:@"Default User"];
JSQMessagesAvatarImage *avatarImage = [[JSQMessagesAvatarImage alloc] initWithAvatarImage:nil highlightedImage:nil placeholderImage:placeHolderImage];

[self.avatarCache setObject:avatarImage forKey:message.user.userID.stringValue];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{

    NSData *data = [NSData dataWithContentsOfURL:imageURL];

    UIImage *imageFromData = [UIImage imageWithData:data];
    UIImage *circularImage = [JSQMessagesAvatarImageFactory circularAvatarHighlightedImage:imageFromData withDiameter:kJSQMessagesCollectionViewAvatarSizeDefault];

    dispatch_async(dispatch_get_main_queue(), ^{

        avatarImage.avatarImage = circularImage;
        avatarImage.avatarHighlightedImage = circularImage;
    });
});
Greg
  • 533
  • 3
  • 14
  • 2
    He asked the question in Swift, please supply in swift – Sauron Jun 22 '16 at 01:01
  • This would update the image in the cache but wouldn't actually update the view on the screen, right? The avatar image object does not seem to observe that property. – Pat Niemeyer Jul 14 '16 at 02:29