重复消息错误
Duplicate Messages Bug
我有一个包含选项卡栏控制器的消息传递应用程序,其中一个选项卡是我使用 JSQMessagesViewController
构建的消息传递视图。当我导航到另一个选项卡并返回消息视图时,我的所有消息都在视图中重复。
此外,当用户发送一条消息时,它会发送同一条消息的多个实例,这显然不是消息传递应用程序的最佳选择。
我尝试将 messages.removeAll()
放在我的 viewDidAppear
中的 observeMessages()
之前,就像用户在此 中建议的那样,它工作了几秒钟,但最终这使得我的应用程序崩溃,控制台中显示以下消息:fatal error: Index out of range
这是我的代码 ViewController
class ChatViewController: JSQMessagesViewController, CLLocationManagerDelegate {
// MARK: Properties
//Location
var city: String = ""
var state: String = ""
var country: String = ""
var locationManager = CLLocationManager()
var locationId: String = ""
func getLocation() -> String {
if city == ("") && state == ("") && country == (""){
return "Anonymous"
}
else {
if country == ("United States") {
return self.city + ", " + self.state
}
else {
return self.city + ", " + self.state + ", " + self.country
}
}
}
//Firebase
var rootRef = FIRDatabase.database().reference()
var messageRef: FIRDatabaseReference!
var locationRef: FIRDatabaseReference!
//JSQMessages
var messages = [JSQMessage]()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = .None
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//collect user's location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
title = "Group Chat"
setupBubbles()
// No avatars
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero
// Remove file upload icon
self.inputToolbar.contentView.leftBarButtonItem = nil;
messageRef = rootRef.child("messages")
locationRef = rootRef.child("locations")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
messages.removeAll()
observeMessages()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//--- CLGeocode to get address of current location ---//
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if let pm = placemarks?.first
{
self.displayLocationInfo(pm)
}
})
}
func displayLocationInfo(placemark: CLPlacemark?)
{
if let containsPlacemark = placemark
{
//stop updating location
locationManager.stopUpdatingLocation()
self.city = (containsPlacemark.locality != nil) ? containsPlacemark.locality! : ""
self.state = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea! : ""
self.country = (containsPlacemark.country != nil) ? containsPlacemark.country! : ""
print(getLocation())
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error while updating location " + error.localizedDescription)
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item] // 1
if message.senderId == senderId { // 2
return outgoingBubbleImageView
} else {
return incomingBubbleImageView
}
}
override func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
private func setupBubbles() {
let factory = JSQMessagesBubbleImageFactory()
outgoingBubbleImageView = factory.outgoingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleBlueColor())
incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleLightGrayColor())
}
func addMessage(id: String, text: String) {
let message = JSQMessage(senderId: id, displayName: "", text: text)
messages.append(message)
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath)
as! JSQMessagesCollectionViewCell
let message = messages[indexPath.item]
if message.senderId == senderId {
cell.textView!.textColor = UIColor.whiteColor()
} else {
cell.textView!.textColor = UIColor.blackColor()
}
return cell
}
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!,
senderDisplayName: String!, date: NSDate!) {
self.edgesForExtendedLayout = .None
let itemRef = messageRef.childByAutoId()
let messageItem = [
"text": text,
"senderId": senderId
]
itemRef.setValue(messageItem)
let locRef = locationRef.childByAutoId()
let locItem = [
senderId : [
"location": getLocation()
]
]
locRef.setValue(locItem)
JSQSystemSoundPlayer.jsq_playMessageSentSound()
finishSendingMessage()
}
private func observeMessages() {
let messagesQuery = messageRef.queryLimitedToLast(25)
messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in
let id = snapshot.value!["senderId"] as! String
let text = snapshot.value!["text"] as! String
self.addMessage(id, text: text)
self.finishReceivingMessage()
}
}
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
}
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = "From: " + getLocation()
if message.senderId == senderId {
return nil
} else {
return NSAttributedString(string: text)
}
}
override func collectionView(collectionView: JSQMessagesCollectionView, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout, heightForCellBottomLabelAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return kJSQMessagesCollectionViewCellLabelHeightDefault
}
}
将observeMessages()
从viewDidAppear()
移到viewDidLoad()
保证它在不同视图之间来回切换或转至 UITableViewController.
时有效
不太确定这是为什么,但我遇到了同样的问题并且设法解决了它,不再有重复的气泡!我认为这是与 UICollectionViewCell 有关的问题,但似乎很容易解决。
让我知道这是否适合你。
在 viewDidDisappear 函数中调用 removeAllObservers 方法。
我遇到了重复消息的问题。 firebase 数据库中的条目很好,没有重复项。 "observe child added" 函数正在生成重复项,即使只有一个子项添加到 firebase 数据库。我检查了每个副本的密钥,它们是相同的。所以,现在我只是检查一条消息是否与前一条消息具有相同的密钥,如果有则将其过滤掉,现在一切正常 - 有点 "hack" 但我无法得到任何其他信息上班。
我有一个包含选项卡栏控制器的消息传递应用程序,其中一个选项卡是我使用 JSQMessagesViewController
构建的消息传递视图。当我导航到另一个选项卡并返回消息视图时,我的所有消息都在视图中重复。
此外,当用户发送一条消息时,它会发送同一条消息的多个实例,这显然不是消息传递应用程序的最佳选择。
我尝试将 messages.removeAll()
放在我的 viewDidAppear
中的 observeMessages()
之前,就像用户在此 fatal error: Index out of range
这是我的代码 ViewController
class ChatViewController: JSQMessagesViewController, CLLocationManagerDelegate {
// MARK: Properties
//Location
var city: String = ""
var state: String = ""
var country: String = ""
var locationManager = CLLocationManager()
var locationId: String = ""
func getLocation() -> String {
if city == ("") && state == ("") && country == (""){
return "Anonymous"
}
else {
if country == ("United States") {
return self.city + ", " + self.state
}
else {
return self.city + ", " + self.state + ", " + self.country
}
}
}
//Firebase
var rootRef = FIRDatabase.database().reference()
var messageRef: FIRDatabaseReference!
var locationRef: FIRDatabaseReference!
//JSQMessages
var messages = [JSQMessage]()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = .None
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//collect user's location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
title = "Group Chat"
setupBubbles()
// No avatars
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero
// Remove file upload icon
self.inputToolbar.contentView.leftBarButtonItem = nil;
messageRef = rootRef.child("messages")
locationRef = rootRef.child("locations")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
messages.removeAll()
observeMessages()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//--- CLGeocode to get address of current location ---//
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if let pm = placemarks?.first
{
self.displayLocationInfo(pm)
}
})
}
func displayLocationInfo(placemark: CLPlacemark?)
{
if let containsPlacemark = placemark
{
//stop updating location
locationManager.stopUpdatingLocation()
self.city = (containsPlacemark.locality != nil) ? containsPlacemark.locality! : ""
self.state = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea! : ""
self.country = (containsPlacemark.country != nil) ? containsPlacemark.country! : ""
print(getLocation())
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error while updating location " + error.localizedDescription)
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item] // 1
if message.senderId == senderId { // 2
return outgoingBubbleImageView
} else {
return incomingBubbleImageView
}
}
override func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
private func setupBubbles() {
let factory = JSQMessagesBubbleImageFactory()
outgoingBubbleImageView = factory.outgoingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleBlueColor())
incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleLightGrayColor())
}
func addMessage(id: String, text: String) {
let message = JSQMessage(senderId: id, displayName: "", text: text)
messages.append(message)
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath)
as! JSQMessagesCollectionViewCell
let message = messages[indexPath.item]
if message.senderId == senderId {
cell.textView!.textColor = UIColor.whiteColor()
} else {
cell.textView!.textColor = UIColor.blackColor()
}
return cell
}
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!,
senderDisplayName: String!, date: NSDate!) {
self.edgesForExtendedLayout = .None
let itemRef = messageRef.childByAutoId()
let messageItem = [
"text": text,
"senderId": senderId
]
itemRef.setValue(messageItem)
let locRef = locationRef.childByAutoId()
let locItem = [
senderId : [
"location": getLocation()
]
]
locRef.setValue(locItem)
JSQSystemSoundPlayer.jsq_playMessageSentSound()
finishSendingMessage()
}
private func observeMessages() {
let messagesQuery = messageRef.queryLimitedToLast(25)
messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in
let id = snapshot.value!["senderId"] as! String
let text = snapshot.value!["text"] as! String
self.addMessage(id, text: text)
self.finishReceivingMessage()
}
}
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
}
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = "From: " + getLocation()
if message.senderId == senderId {
return nil
} else {
return NSAttributedString(string: text)
}
}
override func collectionView(collectionView: JSQMessagesCollectionView, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout, heightForCellBottomLabelAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return kJSQMessagesCollectionViewCellLabelHeightDefault
}
}
将observeMessages()
从viewDidAppear()
移到viewDidLoad()
保证它在不同视图之间来回切换或转至 UITableViewController.
时有效不太确定这是为什么,但我遇到了同样的问题并且设法解决了它,不再有重复的气泡!我认为这是与 UICollectionViewCell 有关的问题,但似乎很容易解决。
让我知道这是否适合你。
在 viewDidDisappear 函数中调用 removeAllObservers 方法。
我遇到了重复消息的问题。 firebase 数据库中的条目很好,没有重复项。 "observe child added" 函数正在生成重复项,即使只有一个子项添加到 firebase 数据库。我检查了每个副本的密钥,它们是相同的。所以,现在我只是检查一条消息是否与前一条消息具有相同的密钥,如果有则将其过滤掉,现在一切正常 - 有点 "hack" 但我无法得到任何其他信息上班。