显示用户位置
Displaying user location
我有一个使用 JSQMessagesViewController 框架构建的简单消息传递应用程序。我一直在尝试使用 CoreLocation 来收集用户的位置,并且我想在实际消息内容下显示该位置 (attributedTextForCellBottomLabelAtIndexPath
)。我已经解决了所有这些问题,但问题是每个位置对于每条消息都必须是唯一的,这是行不通的。
例如,如果用户从西雅图发送消息,则消息下方应显示 "Seattle"。如果另一个用户向 San Fransisco 发送另一条消息,它应该在他们的消息下方显示 "San Fransisco"。
现在发生的事情是,所有消息都说它们来自用户当前所在的位置(假设他们在西雅图)。所以不管对方是不是在旧金山,消息都会显示是西雅图。
这是一个非常令人困惑的问题,但我认为应该有一个简单的解决办法...
这是 attributedTextForCellBottomLabelAtIndexPath
的代码:
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = getLocation()
if message.senderId == senderId {
return nil
} else {
return NSAttributedString(string: text)
}
}
注意:我有一个名为 getLocation()
的函数,它只是 returns 用户的 city/state/country
下面是 class ChatViewController
上下文的其余代码:
/*
* Copyright (c) 2016 Ahad Sheriff
*/
import UIKit
import Firebase
import JSQMessagesViewController
import CoreLocation
class ChatViewController: JSQMessagesViewController, CLLocationManagerDelegate {
// MARK: Properties
//Firebase
var rootRef = FIRDatabase.database().reference()
var messageRef: FIRDatabaseReference!
var locationRef: FIRDatabaseReference!
//JSQMessages
var messages = [JSQMessage]()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
//Location
var city: String = ""
var state: String = ""
var country: String = ""
var locationManager = CLLocationManager()
var locationId: String = ""
func getLocation() -> String {
if city == ("") && state == ("") && country == (""){
return "Planet Earth"
}
else {
if country == ("United States") {
return self.city + ", " + self.state
}
else {
return self.city + ", " + self.state + ", " + self.country
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Initialize location
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//collect user's location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
title = "Chat"
setupBubbles()
// No avatars
// Remove file upload icon
self.inputToolbar.contentView.leftBarButtonItem = nil;
// Send button
self.inputToolbar.contentView.rightBarButtonItem.setTitle("Send", forState: UIControlState.Normal)
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero
//Firebase reference
messageRef = rootRef.child("messages")
locationRef = rootRef.child("locations")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
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 { // 3
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(
purp)
incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
redish)
}
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.whiteColor()
}
return cell
}
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!,
senderDisplayName: String!, date: NSDate!) {
let itemRef = messageRef.childByAutoId() // 1
let messageItem = [ // 2
"text": text,
"senderId": senderId
]
itemRef.setValue(messageItem) // 3
let locRef = locationRef.childByAutoId()
let locItem = [
senderId : [
"location": getLocation()
]
]
locRef.setValue(locItem)
// 4
JSQSystemSoundPlayer.jsq_playMessageSentSound()
// 5
finishSendingMessage()
Answers.logCustomEventWithName("Message sent", customAttributes: nil)
}
private func observeMessages() {
// 1
let messagesQuery = messageRef.queryLimitedToLast(25)
// 2
messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in
// 3
let id = snapshot.value!["senderId"] as! String
let text = snapshot.value!["text"] as! String
// 4
self.addMessage(id, text: text)
// 5
self.finishReceivingMessage()
Answers.logCustomEventWithName("Visited RoastChat", customAttributes: nil)
}
}
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = 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
}
}
注意:您也可以忽略我将用户位置保存到 Firebase 的代码。
提前感谢您的帮助!如果您还有其他问题,请告诉我。
您的问题是您设置的是客户端底部消息标签,而不是消息中的数据。所以每个底部标签都是由用户而不是消息的发起者设置的。
我不知道那之后是否成功,但本质上你是在本地获取位置数据,所以如果你在西雅图的 Mark
和不在西雅图的 Deric
之间进行对话西雅图。
EX.Conversation
马克嗨
Deric Wazzup
它总是会说西雅图,因为您正在本地从客户端获取位置数据并在此函数中进行设置。
func getLocation()
因此,当您查看来自 Mark
的 phone 的对话时,您正在根据 Mark
的位置设置位置数据,即使对于来自 'Deric' 的消息也是如此].
所以解决这个问题的方法是将位置数据保存到消息中并存储在firebase中。然后更改此功能以提取底部标签的数据。类似的东西。
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = message.location <- Here is a suggested solution ****
if message.senderId == senderId {
return nil
} else {
return NSAttributedString(string: text)
}
}
我希望这很清楚。我知道这是一个令人困惑的问题和几乎同样令人困惑的答案。但本质上你需要将位置保存到 messageData post 它到你的后端 firebase
然后显示给定消息。
我认为你绝对是在正确的道路上,只是错过了那一件。
编辑:
我会看看这个
https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html
但是为了简单起见,我只解析位置数据并将其保存到 firebase
上的消息对象中,这样当您尝试显示留言。
我有一个使用 JSQMessagesViewController 框架构建的简单消息传递应用程序。我一直在尝试使用 CoreLocation 来收集用户的位置,并且我想在实际消息内容下显示该位置 (attributedTextForCellBottomLabelAtIndexPath
)。我已经解决了所有这些问题,但问题是每个位置对于每条消息都必须是唯一的,这是行不通的。
例如,如果用户从西雅图发送消息,则消息下方应显示 "Seattle"。如果另一个用户向 San Fransisco 发送另一条消息,它应该在他们的消息下方显示 "San Fransisco"。
现在发生的事情是,所有消息都说它们来自用户当前所在的位置(假设他们在西雅图)。所以不管对方是不是在旧金山,消息都会显示是西雅图。
这是一个非常令人困惑的问题,但我认为应该有一个简单的解决办法...
这是 attributedTextForCellBottomLabelAtIndexPath
的代码:
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = getLocation()
if message.senderId == senderId {
return nil
} else {
return NSAttributedString(string: text)
}
}
注意:我有一个名为 getLocation()
的函数,它只是 returns 用户的 city/state/country
下面是 class ChatViewController
上下文的其余代码:
/*
* Copyright (c) 2016 Ahad Sheriff
*/
import UIKit
import Firebase
import JSQMessagesViewController
import CoreLocation
class ChatViewController: JSQMessagesViewController, CLLocationManagerDelegate {
// MARK: Properties
//Firebase
var rootRef = FIRDatabase.database().reference()
var messageRef: FIRDatabaseReference!
var locationRef: FIRDatabaseReference!
//JSQMessages
var messages = [JSQMessage]()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
//Location
var city: String = ""
var state: String = ""
var country: String = ""
var locationManager = CLLocationManager()
var locationId: String = ""
func getLocation() -> String {
if city == ("") && state == ("") && country == (""){
return "Planet Earth"
}
else {
if country == ("United States") {
return self.city + ", " + self.state
}
else {
return self.city + ", " + self.state + ", " + self.country
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Initialize location
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//collect user's location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
title = "Chat"
setupBubbles()
// No avatars
// Remove file upload icon
self.inputToolbar.contentView.leftBarButtonItem = nil;
// Send button
self.inputToolbar.contentView.rightBarButtonItem.setTitle("Send", forState: UIControlState.Normal)
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero
//Firebase reference
messageRef = rootRef.child("messages")
locationRef = rootRef.child("locations")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
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 { // 3
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(
purp)
incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
redish)
}
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.whiteColor()
}
return cell
}
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!,
senderDisplayName: String!, date: NSDate!) {
let itemRef = messageRef.childByAutoId() // 1
let messageItem = [ // 2
"text": text,
"senderId": senderId
]
itemRef.setValue(messageItem) // 3
let locRef = locationRef.childByAutoId()
let locItem = [
senderId : [
"location": getLocation()
]
]
locRef.setValue(locItem)
// 4
JSQSystemSoundPlayer.jsq_playMessageSentSound()
// 5
finishSendingMessage()
Answers.logCustomEventWithName("Message sent", customAttributes: nil)
}
private func observeMessages() {
// 1
let messagesQuery = messageRef.queryLimitedToLast(25)
// 2
messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in
// 3
let id = snapshot.value!["senderId"] as! String
let text = snapshot.value!["text"] as! String
// 4
self.addMessage(id, text: text)
// 5
self.finishReceivingMessage()
Answers.logCustomEventWithName("Visited RoastChat", customAttributes: nil)
}
}
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = 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
}
}
注意:您也可以忽略我将用户位置保存到 Firebase 的代码。
提前感谢您的帮助!如果您还有其他问题,请告诉我。
您的问题是您设置的是客户端底部消息标签,而不是消息中的数据。所以每个底部标签都是由用户而不是消息的发起者设置的。
我不知道那之后是否成功,但本质上你是在本地获取位置数据,所以如果你在西雅图的 Mark
和不在西雅图的 Deric
之间进行对话西雅图。
EX.Conversation
马克嗨
Deric Wazzup
它总是会说西雅图,因为您正在本地从客户端获取位置数据并在此函数中进行设置。
func getLocation()
因此,当您查看来自 Mark
的 phone 的对话时,您正在根据 Mark
的位置设置位置数据,即使对于来自 'Deric' 的消息也是如此].
所以解决这个问题的方法是将位置数据保存到消息中并存储在firebase中。然后更改此功能以提取底部标签的数据。类似的东西。
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item]
// Call data I have retrieved below with message
let text = message.location <- Here is a suggested solution ****
if message.senderId == senderId {
return nil
} else {
return NSAttributedString(string: text)
}
}
我希望这很清楚。我知道这是一个令人困惑的问题和几乎同样令人困惑的答案。但本质上你需要将位置保存到 messageData post 它到你的后端 firebase
然后显示给定消息。
我认为你绝对是在正确的道路上,只是错过了那一件。
编辑: 我会看看这个 https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html
但是为了简单起见,我只解析位置数据并将其保存到 firebase
上的消息对象中,这样当您尝试显示留言。