通过聊天发送图像
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
如何使用 XMPPFramework iOS 发送图像(图像 url)、视频聊天 App 消息