通过聊天发送图像

Posted

技术标签:

【中文标题】通过聊天发送图像【英文标题】:Send image through chat 【发布时间】:2018-01-23 03:26:00 【问题描述】:

我想将图像发送到 firebase,然后将它们加载到聊天气泡中。现在,在用户选择图像后,它会加载到 firebase 中,并且 JSQPhotoMediaItem 在聊天中显示图像。然而,其他用户只看到一个空气泡,当我重新加载视图时,它也显示为我端的空白气泡。如何修复空气泡并使用 Firebase 中的照片网址填充它。

class ChatViewController: JSQMessagesViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate 

let incomingBubble = JSQMessagesBubbleImageFactory(bubble: UIImage.jsq_bubbleCompactTailless(), capInsets: UIEdgeInsets.zero).incomingMessagesBubbleImage(with: UIColor(white: 0.90, alpha: 1.0))
let incomingBubbleWithTail = JSQMessagesBubbleImageFactory().incomingMessagesBubbleImage(with: UIColor(white: 0.90, alpha: 1.0))
let outgoingBubble = JSQMessagesBubbleImageFactory(bubble: UIImage.jsq_bubbleCompactTailless(), capInsets: UIEdgeInsets.zero).outgoingMessagesBubbleImage(with: UIColor.red)
let outgoingBubbleWithTail = JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImage(with: UIColor.red)

var messages:[JSQMessage]!

var conversation:Conversation!
var conversationKey:String!
var partner:Users!
var partnerImage:UIImage?

var downloadRef:DatabaseReference?


@objc func handleUploadTap()

    let imagePickerController = UIImagePickerController()
    imagePickerController.allowsEditing = true
    imagePickerController.delegate = self

    present(imagePickerController, animated: true, completion: nil)
    print("image tapped")


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) 

    var selectedImageFromPicker: UIImage?
    if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage
        selectedImageFromPicker = editedImage
    else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage
        selectedImageFromPicker = originalImage
    
    if let selectedImage = selectedImageFromPicker

        let mediaItem = JSQPhotoMediaItem(image: nil)
        mediaItem?.appliesMediaViewMaskAsOutgoing = true
        mediaItem?.image = UIImage(data: UIImageJPEGRepresentation(selectedImage, 0.5)!)
        let sendMessage = JSQMessage(senderId: senderId, displayName: self.senderId, media: mediaItem)
        self.messages.append(sendMessage!)
        self.finishSendingMessage()
        uploadToFirebaseStorageUsingImage(image: selectedImage)
    
    dismiss(animated: true, completion: nil)


private func uploadToFirebaseStorageUsingImage(image: UIImage)
    let imageName = NSUUID().uuidString
    let ref = Storage.storage().reference().child("message_images").child(imageName)
    if let uploadData = UIImageJPEGRepresentation(image, 0.3)

        ref.putData(uploadData, metadata: nil, completion:  (metadata, error) in

            if error != nil 
                print("failed to load:", error)
                return
            

            if let imageUrl = metadata?.downloadURL()?.absoluteString

                self.sendMessageWithImageUrl(imageUrl: imageUrl)
            
        )


private func sendMessageWithImageUrl(imageUrl: String)

    guard let user = currentUser else  return 
    let ref = Database.database().reference().child("conversations/threads/\(conversation.key)").childByAutoId()
    let messageObject = [
        "text":" ",
        "recipient": conversation.partner_uid,
        "sender":user.uid,
        "senderName": user.firstLastName,
        "imageUrl":imageUrl,
        "timestamp": [".sv":"timestamp"]
        ] as [String:Any]
    ref.setValue(messageObject, withCompletionBlock:  error, ref in

    )

    return self.finishSendingMessage(animated: true)


func imagePickerControllerDidCancel(_ picker: UIImagePickerController) 
    dismiss(animated: true, completion: nil)










override func viewDidLoad() 
    super.viewDidLoad()

    navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: self, action: #selector(handleDismiss))
    view.backgroundColor = UIColor(white: 1.0, alpha: 1.0)

    self.senderDisplayName = ""
    if let user = Auth.auth().currentUser 
        self.senderId = user.uid
     else 
        self.senderId = ""
    

    messages = [JSQMessage]()

    let addImage = self.inputToolbar.contentView.leftBarButtonItem
    addImage?.isUserInteractionEnabled = true
    addImage?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleUploadTap)))


    self.inputToolbar.contentView.rightBarButtonItem.setTitleColor(UIColor.red, for: .normal)
  //  self.inputToolbar.contentView.leftBarButtonItemWidth = 0
    self.inputToolbar.contentView.textView.placeHolder = "New message"
    self.inputToolbar.contentView.textView.keyboardAppearance = .light

    //collectionView?.collectionViewLayout.incomingAvatarViewSize = CGSize(width: 32, height: 32)
    collectionView?.collectionViewLayout.outgoingAvatarViewSize = .zero


    collectionView?.collectionViewLayout.springinessEnabled = true
    collectionView?.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
    collectionView?.reloadData()

      title = partner.firstLastName
    conversation.printAll()
    downloadRef = Database.database().reference().child("conversations/threads/\(conversation.key)")
    downloadMessages()






@objc func handleDismiss() 
    self.dismiss(animated: true, completion: nil)



override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    conversation.printAll()


override func viewWillDisappear(_ animated: Bool) 
    super.viewWillDisappear(animated)


override func viewDidDisappear(_ animated: Bool) 
    super.viewDidDisappear(animated)
    downloadRef?.removeAllObservers()


override func viewDidAppear(_ animated: Bool) 
    super.viewDidAppear(animated)


override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return self.messages.count


override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! 
    let data = self.messages[indexPath.row]
    return data


override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! 
    let data = messages[indexPath.row]
    switch(data.senderId) 
    case self.senderId:
        return self.outgoingBubble
    default:
        return self.incomingBubble
    



override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! 
    let data = messages[indexPath.row]
    switch(data.senderId) 
    case self.senderId:
        return nil
    default:
        if partnerImage != nil 
            let image = JSQMessagesAvatarImageFactory.avatarImage(with: partnerImage!, diameter: 48)
            return image
        

        return nil
    



override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell

    let data = messages[indexPath.row]
    switch(data.senderId) 
    case self.senderId:
        cell.textView?.textColor = UIColor.white
    default:
        cell.textView?.textColor = UIColor.black
    
    return cell


override func collectionView

    (_ collectionView: JSQMessagesCollectionView!, attributedTextForCellTopLabelAt indexPath: IndexPath!) -> NSAttributedString! 
    let currentItem = self.messages[indexPath.item]

    if indexPath.item == 0 && messages.count > 8 
        return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: currentItem.date)
    


    if indexPath.item > 0 
        let prevItem    = self.messages[indexPath.item-1]

        let gap = currentItem.date.timeIntervalSince(prevItem.date)

        if gap > 1800 
            return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: currentItem.date)
        
     else 
        return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: currentItem.date)
    


    return nil



override func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForCellTopLabelAt indexPath: IndexPath!) -> CGFloat 

    if indexPath.item == 0 && messages.count > 8 
        return kJSQMessagesCollectionViewCellLabelHeightDefault
    

    if indexPath.item > 0 
        let currentItem = self.messages[indexPath.item]
        let prevItem    = self.messages[indexPath.item-1]

        let gap = currentItem.date.timeIntervalSince(prevItem.date)

        if gap > 1800 
            return kJSQMessagesCollectionViewCellLabelHeightDefault
        

        if prevItem.senderId != currentItem.senderId 
            return 1.0
         else 
            return 0.0
        
      else 
        return kJSQMessagesCollectionViewCellLabelHeightDefault
    




override func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForCellBottomLabelAt indexPath: IndexPath!) -> CGFloat 
    return 0.0



override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) 
    guard let user = currentUser else  return 
    let ref = Database.database().reference().child("conversations/threads/\(conversation.key)").childByAutoId()
    let messageObject = [
        "recipient": conversation.partner_uid,
        "sender":user.uid,
        "senderName": user.firstLastName,
        "text":text,
        "timestamp": [".sv":"timestamp"],
        "imageUrl": " "
    ] as [String:Any]
    ref.setValue(messageObject, withCompletionBlock:  error, ref in

    )

    return self.finishSendingMessage(animated: true)


func downloadMessages() 

    self.messages = []

    downloadRef?.observe(.childAdded, with:  snapshot in
        let dict = snapshot.value as! [String:AnyObject]

        let recipient  = dict["recipient"] as! String
        let sender  = dict["sender"] as! String
        let text  = dict["text"] as! String
        let timestamp = dict["timestamp"] as! Double
         let imageUrl = dict["imageUrl"] as! String
        let date = NSDate(timeIntervalSince1970: timestamp/1000)

        var img: UIImage?
        let mediaItem = JSQPhotoMediaItem(image: nil)
        mediaItem?.appliesMediaViewMaskAsOutgoing = (id == self.senderId)

        let message = JSQMessage(senderId: sender, senderDisplayName: "", date: date as Date!, text: text, media: mediaItem)
        if img != nil
            mediaItem?.image = img! as UIImage
            self.collectionView!.reloadData()

        

        self.messages.append(message!)
        self.reloadMessagesView()
        self.finishReceivingMessage(animated: true)


    )


func reloadMessagesView() 
    self.collectionView?.reloadData()

    guard let user = Auth.auth().currentUser else return 

    let ref = Database.database().reference().child("conversations/users/\(user.uid)/\(conversation.partner_uid)/seen")
    ref.setValue(true)


【问题讨论】:

【参考方案1】:

在您的函数 downloadMessages() 中,您将获得 imageUrl

let imageUrl = dict["imageUrl"] as! String

但是当您将消息附加到消息时,您对那个 imageUrl 什么都不做?

let message = JSQMessage(senderId: sender, senderDisplayName: "", date: date as Date!, text: text )
self.messages.append(message!)

你的带有图片的 JSQMessage 应该包含一个 mediaItem (JSQPhotoMediaItem,否则你只是在加载一个空消息

var img: UIImage?
let mediaItem = JSQPhotoMediaItem(image: nil) //not a string
mediaItem?.appliesMediaViewMaskAsOutgoing = (id == self.senderId)

message = JSQMessage(senderId: id, senderDisplayName: name, date: date, media: mediaItem)

//get img from firebase, with whatever method you use
if img != nil 
    mediaItem?.image = img! as UIImage
    self.collectionView!.reloadData()


self.messages.append(message)
//etc

【讨论】:

无法使用类型为“(senderId: String, senderDisplayName: String, date: Date!, text: String, JSQMediaItem: String)”类型的参数列表调用类型“JSQMessage”的初始化程序 如前所述,JSQMediaItem 是一个 JSQPhotoMediaItem,您必须从服务器 (firebase) 下载图像。更新答案 如果我将图像作为字符串检索,这会起作用吗? 如果您将 UIImage 保存为字符串(URL?),您应该知道如何将其反转为 UIImage(从远程 url 加载图像),这正是您所需要的。或者你的意思是***.com/questions/11251340/…【参考方案2】:

您可能想要跟踪上传任务:StorageReference.putData 返回UploadTask。在该任务中添加onCompleteListener(或onSuccessListener,我不记得确切),完成后,您可以检索uri并发送消息。 这适用于 android,但适用于 ios

【讨论】:

以上是关于通过聊天发送图像的主要内容,如果未能解决你的问题,请参考以下文章

使用 QuickBlox 聊天 SDK 发送图像 - Android

使用 strophe js 在聊天中发送图像

如何使用 XMPPFramework iOS 发送图像(图像 url)、视频聊天 App 消息

在聊天应用程序中使用 Web 套接字 Api 无法正确显示图像

套接字编程/通过无线发送二进制图像数据

如何在聊天应用程序中发送媒体内容?