Search code examples
iosswiftjsqmessagesviewcontroller

Assigning avatars from URL string in JSQMessagesController


I couldn't find much of use in the docs, and the sample project in Swift only had hardcoded avatars so that wasn't much help either. I also saw this question but that example was also not applicable in my situation.

I have my user sign in with Facebook, and a graph request grabs their profile picture which is then stored in Firebase and a User object as a string (the URL path to the image I mean).

This is my cellForItem function

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
    let message = messages[indexPath.item]

    if message.senderId == senderId {
        cell.textView?.textColor = UIColor.white
    } else {
        cell.textView?.textColor = UIColor.black
    }
    return cell
}

And this is where I should be setting the avatars:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {

    let message = messages[indexPath.item]

    if let messageID = message.senderId {
        let users = [User]()
        let userProfilePictures = users[indexPath.row].profilePicture
    }

    return ????
}

Anyone know how I can get the profile pictures set to the correct avatars?

EDIT: This is my image caching code:

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

func loadImageUsingCacheWithUrlString(_ urlString: String) {

    self.image = nil

    // Check cache for image first
    if let cachedImage = imageCache.object(forKey: urlString as NSString) {
        self.image = cachedImage
        return
    }

    // Otherwise fire off a new download
    Alamofire.request(urlString)
        .responseImage { response in

            if let downloadedImage = response.result.value {
                // image is here.
                imageCache.setObject(downloadedImage, forKey: urlString as NSString)
                self.image = downloadedImage
            }
        }
}

}


Solution

  • Ya, What you are looking for is the 'JSQMessagesAvatarImageFactory' Documentation Link

    Since you have your userProfilePicture Then you just need to get the image data for that url. You can do this a number of ways I personally love to use a framework that handles catching for you. SDWebImage or AlamofireImage or how ever else you want to get that data.

    I like AlimofireImage so that is what I will demonstrate here. To make it so that I do not have to keep making downloading request for each cell I save them in a dictionary using the senderID as a key. If the dictionary entry is there I don't make the request. Define your dictionary.

    var avatarDictionary: [String:UIImage] = [:]
    

    Then after retrieving all the messages for the conversation call getAllAvatars()

    func getAllAvatars() {
        for message in messages {
            guard let key = message.senderID else { return }
            if avatarDictionary[key] == nil {
                try? _ = imageDownloader?.download(URLRequest(url: API.userPhoto(userID: key).asURL()), completion: { (response) in
                    guard let image = response.result.value else { return }
                    self.avatarDictionary[key] = image
                    self.collectionView.reloadData()
                })
            }
        }
    }
    

    API.userPhoto(userID: key).asURL() is the url to your image data source so this is what you will change for the url you have.

    There are a few other methods you could also take a look into. I made my own function to give me a defaultAvatar for users with no image. It looks like this

    func blankAvatar(message: Message) -> JSQMessageAvatarImageDataSource {
        return JSQMessagesAvatarImageFactory.avatarImage(withUserInitials: initialsFrom(firstName: message.fistName, lastName: message.lastName), backgroundColor: UIColor.gray, textColor: UIColor.white, font: UIFont.systemFont(ofSize: 14), diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault))
    }
    

    Then you can return a image if it is there or a blank avatar if not from the avatarImageDataForItemAt method

    return avatarDictionary[id] == nil ? blankAvatar(message: message) : JSQMessagesAvatarImageFactory.avatarImage(with: avatarDictionary[id], diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault))
    

    Also don't forget to set the size of the avatars for the collectionView

     collectionView?.collectionViewLayout.incomingAvatarViewSize = avatarSize
     collectionView?.collectionViewLayout.outgoingAvatarViewSize = avatarSize
    

    or they wont show at all.

    Let me know if you need more help 🖖🏽.