MessageKit - How to async load images in subclass of MessagesViewController?
Asked Answered
P

2

7

Recently I have been developing a chat application and was having trouble asynchronously loading images with the chat.

Working with MessageKit - 2.0

I had tried this

import MessageKit

class Image: MediaItem {
  var url: URL?

  var image: UIImage?

  var placeholderImage: UIImage

  var size: CGSize

  init(url: URL) {
    self.url = url
    self.size = CGSize(width: 240, height: 240)
    self.placeholderImage = UIImage()
    DispatchQueue.global().async {
        if let data = try? Data(contentsOf: url) {
            if let image = UIImage(data: data) {
                DispatchQueue.main.async {
                    self.image = image
                }
            }
        }
    }
}

init(image: UIImage) {
    self.image = image
    self.size = CGSize(width: 240, height: 240)
    self.placeholderImage = UIImage()
}

}

Then I initialize the Image MessageType with kind = .photo(Image(url: url))

This doesn't seem to work.

Physiology answered 3/1, 2019 at 7:17 Comment(0)
P
16

I have since learnt that MessageKit provides a delegate method in MessagesDisplayDelegate called

func configureMediaMessageImageView(_ imageView: UIImageView,
                                    for message: MessageType,
                                    at indexPath: IndexPath,
                                    in messagesCollectionView: MessagesCollectionView)

We can asynchronously load images for given message with this delegate method.

I am using a UIImageView extension to help with this

import UIKit

extension UIImageView {
  func load(url: URL) {
    DispatchQueue.global().async { [weak self] in
        if let data = try? Data(contentsOf: url) {
            if let image = UIImage(data: data) {
                DispatchQueue.main.async {
                    self?.image = image
                }
            }
        }
     }
   }
 }

So the solution is as follows

func configureMediaMessageImageView(_ imageView: UIImageView,
                                    for message: MessageType,
                                    at indexPath: IndexPath,
                                    in messagesCollectionView: MessagesCollectionView) {
    /*acquire url for the image in my case i had a 
    custom type Message which stored  the image url */
    guard
        let msg = message as? Message,
        let url = msg.downloadURL
    else { return }
    imageView.load(url: url)
}
Physiology answered 3/1, 2019 at 7:29 Comment(5)
This works, what I ended up adding was a messageImage property to Message, so once the image is downloaded I stored it there and then check for that property to avoid unnecessary request to download the image.Breda
Thank you, Paul and Lucho. I was having difficulties trying to make images download async in MessageKit's VC. I've taken both of your suggestions and implemented the logic in my app. Works like a charm!Agrarian
That is the solution!Collum
@Paul I'm working on the same problem. Would you be open to chat?Tonytonya
Any way to set the image size after the async download? I'd rather not have it fixed to something like 240, 240.Quebec
B
0

Safer way to what @Paul Nyondo provided. Getting and setting image from a URL while accessing message item data (MessageType) and no need to check if is a type of 'Message'.

1.- Go to your file when you add delegates of "MessagesDisplayDelegate" and add this method:

func configureMediaMessageImageView(_ imageView: UIImageView,
                                    for message: MessageType,
                                    at indexPath: IndexPath,
                                    in messagesCollectionView: MessagesCollectionView) {
    if case let .photo(mediaItem) = message.kind, let imageURL = mediaItem.url {
        imageView.kf.setImage(with: imageURL)
    }
}

2.- Helper method to download image in an async way:

import UIKit

extension UIImageView {
  func load(url: URL) {
    DispatchQueue.global().async { [weak self] in
        if let data = try? Data(contentsOf: url) {
            if let image = UIImage(data: data) {
                DispatchQueue.main.async {
                    self?.image = image
                }
            }
        }
     }
   }
 }
Betz answered 13/10, 2022 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.