下載訊息
下載訊息的程式將分成兩個部分,第一個部分是下載歷史訊息,第二個部分則是取得最新訊息
下載歷史訊息的部分,在viewDidLoad中messageRef = FIRDatabase.database().reference().child("messages/(roomID!)")之後加入
messageRef.queryOrdered(byChild: "date").observeSingleEvent(of: .value, with: {[weak self] (snapshot) in
if !snapshot.hasChildren() { return }
// messages 架構為
// messageId1
// |- messageData1
// messageId2
// |- messageData2
snapshot.children.forEach({ (child) in
guard let childSnap = child as? FIRDataSnapshot else { return }
guard let msgData = childSnap.value as? [String: Any] else { return }
let msgId = childSnap.key
var message = msgData
message["id"] = msgId
self?.messages.append(message)
})
self?.collectionView?.reloadData()
guard let interval = self?.messages.last?["date"] as? TimeInterval else { return }
self?.messageRef.queryOrdered(byChild: "date").queryStarting(atValue: interval + 0.001).observe(.childAdded, with: {[weak self] (snapshot) in
guard let msgData = snapshot.value as? [String: Any], let senderId = msgData["senderId"] as? String else {
return
}
// 如果是自己傳的訊息就跳過 (因未之前傳送訊息時就有加入dataSource了)
if senderId == self?.senderId() {
return
}
var message = msgData
message["id"] = snapshot.key
self?.messages.append(message)
self?.finishReceivingMessage()
})
})
接著還要實作JSQ中的dataSource才能夠正常顯示訊息,在程式碼最底下加入extension並將需要實作的func加入
extension ChatViewController {
// 訊息數量
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
// 將訊息資料轉換成JSQ指定的Model
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageDataForItemAt indexPath: IndexPath) -> JSQMessageData {
let message = messages[indexPath.item]
guard let text = message["text"] as? String,
let senderId = message["senderId"] as? String,
let senderDisplayName = message["senderDisplayName"] as? String,
let interval = message["date"] as? TimeInterval
else { fatalError("data format exception") }
let date = Date(timeIntervalSince1970: interval)
return JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text)
}
// 泡泡框
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageBubbleImageDataForItemAt indexPath: IndexPath) -> JSQMessageBubbleImageDataSource? {
// TODO: 訊息會自動分邊,但目前泡泡框都會只向右側,稍候修正
return JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImage(with: UIColor.green)
}
// 頭像 - 此範例不顯示頭像
override func collectionView(_ collectionView: JSQMessagesCollectionView, avatarImageDataForItemAt indexPath: IndexPath) -> JSQMessageAvatarImageDataSource? {
return nil
}
}
實作到這邊,如果有iOS裝置就可以將APP運行到裝置中,試著和模擬器互相傳送訊息
首先,讓泡泡框的方向正確,在func viewDidLoad()上方加入
lazy var incomingBubbleImage: JSQMessageBubbleImageDataSource = {[unowned self] in
let factory = JSQMessagesBubbleImageFactory()
return factory.incomingMessagesBubbleImage(with: UIColor.darkGray)
}()
lazy var outgoingBubbleImage: JSQMessageBubbleImageDataSource = {[unowned self] in
let factory = JSQMessagesBubbleImageFactory()
return factory.outgoingMessagesBubbleImage(with: UIColor.lightGray)
}()
並修改extension中泡泡框的dataSource
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageBubbleImageDataForItemAt indexPath: IndexPath) -> JSQMessageBubbleImageDataSource? {
let message = messages[indexPath.item]
if message["senderId"] as? String == self.senderId() {
return outgoingBubbleImage
} else {
return incomingBubbleImage
}
}
修改後再次運行APP
因為聊天室可能一次有很多人進來,如果不標示的話使用者會難以辨識說話的對象,因此接下來需要幫incoming的訊息標上名字
在extension中,追加下列func實作
override func collectionView(_ collectionView: JSQMessagesCollectionView, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout, heightForMessageBubbleTopLabelAt indexPath: IndexPath) -> CGFloat {
return 30
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath) -> NSAttributedString? {
let message = messages[indexPath.item]
if message["senderId"] as? String == self.senderId() {
return nil
}
guard let displayName = message["senderDisplayName"] as? String else {
return NSAttributedString(string: "未知")
}
return NSAttributedString(string: displayName)
}
成果圖
由於這個範例還沒有放上美美的頭貼,剛剛實作dataSource雖然已經告訴JSQMessagesViewController不提供頭貼,但是他還是幫我們保留了左右兩側顯示頭貼的位置,在viewDidLoad內最後面加入下列程式,可以將兩邊多餘的空白移除
collectionView?.collectionViewLayout.incomingAvatarViewSize = .zero
collectionView?.collectionViewLayout.outgoingAvatarViewSize = .zero
然而我們也還沒有實作傳送附加檔案功能,至時候如果去點了迴紋針圖示App將Crash,在上面兩行程式碼之後再加入下列程式來隱藏附加檔案按鈕
inputToolbar.contentView?.leftBarButtonItem = nil
別忘了還要在deinit時釋放observer
deinit {
messageRef.removeAllObservers()
}
完全體
文字聊天功能到此完成,程式碼下載