0

我正在使用消息傳遞應用程序,並且想要顯示消息內容的消息發送位置。我嘗試將用戶的位置數據發送到Firebase,然後嘗試檢索數據以將其顯示爲字符串。從Firebase中檢索文本時未顯示在應用程序中

獲取用戶位置正常(我使用CoreLocation這樣做),因爲上傳數據到我的Firebase實時數據庫。餘與每個消息一起保存的位置爲這樣:

讓的itemref = messageRef.childByAutoId()// 1 讓messageItem = [// 2 「文本」:文本, 「senderId」:senderId, 「位置「:的getLocation() ] itemRef.setValue(messageItem)// 3

,然後嘗試檢索的另一種方法將數據作爲這樣的:

override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! { 

     var locationId: String = "" 
     let messagesQuery = messageRef 
     let message = messages[indexPath.item] 

     messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in 
      locationId = snapshot.value!["location"] as! String 
      print(locationId) 
     } 

     if message.senderId == senderId { 
      return nil 
     } else { 
      return NSAttributedString(string: locationId) 
     } 

    } 

正確的位置被打印到安慰但沒有任何顯示在我的應用程序中。但是,如果我使用任何其他字符串替換變量locationId,它將起作用。

我的問題我相信是與Firebase檢索。

如果有人能幫我解決這個問題,那將不勝感激。

這裏是代碼的其餘部分我的類引用(以防萬一):

類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! 

    var purp = UIColor.init(red:47/255, green: 53/255, blue: 144/255, alpha: 1) 
    var roastish = UIColor.init(red: 255/255, green: 35/255, blue: 35/255, alpha: 1.0) 
    var orangish = UIColor.init(red: 231/255, green: 83/255, blue: 55/255, alpha: 1.0) 
    var gray = UIColor.init(red: 241/255, green: 251/255, blue: 241/255, alpha: 1) 

    //Location 
    var city: String = "" 
    var state: String = "" 
    var country: String = "" 
    var locationManager = CLLocationManager() 

    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() 
     } 

     // Change the navigation bar background color 
     navigationController!.navigationBar.barTintColor = gray 

     self.navigationController!.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "Avenir Next", size: 20)!] 

     title = "RoastChat" 
     setupBubbles() 
     // No avatars 

     // Remove file upload icon 
     self.inputToolbar.contentView.leftBarButtonItem = nil; 
     // Send button 
     self.inputToolbar.contentView.rightBarButtonItem.setTitle("Roast", forState: UIControlState.Normal) 
     // Send button color 
     self.inputToolbar.contentView.rightBarButtonItem.setTitleColor(roastish, forState: UIControlState.Normal) 
     // Input bar text placeholder 
     self.inputToolbar.contentView.textView.placeHolder = "RoastChat" 

     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! : "" 

     } 

    } 


    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(
      UIColor.jsq_messageBubbleBlueColor()) 
     incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
      roastish) 
    } 

    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, 
      "location": getLocation() 
     ] 
     itemRef.setValue(messageItem) // 3 

     // 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! { 

     var locationId: String = "" 
     let messagesQuery = messageRef 
     let message = messages[indexPath.item] 

     messagesQuery.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in 
      locationId = snapshot.value!["location"] as! String 
      print(locationId) 
     } 

     if message.senderId == senderId { 
      return nil 
     } else { 
      return NSAttributedString(string: locationId) 
     } 

    } 

    override func collectionView(collectionView: JSQMessagesCollectionView, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout, heightForCellBottomLabelAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
     return kJSQMessagesCollectionViewCellLabelHeightDefault 
    } 


    override func collectionView(collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAtIndexPath indexPath: NSIndexPath!) { 

     super.collectionView(collectionView, didTapMessageBubbleAtIndexPath: indexPath) 
     let data = self.messages[indexPath.row] 
     print("They tapped: " + (data.text)) 

    } 

} 

回答

0

我無法以其他方式進行檢索。相反,我利用其他內置方法來解決我的問題的JSQMessagesViewControllers。該框架有一個名爲senderDisplayName的內置變量。由於這個應用程序的性質是匿名的,我不需要使用它,所以我操縱了senderDisplay名稱來顯示用戶的位置。

func addMessage(id: String, text: String, displayName: String) { 
    let message = JSQMessage(senderId: id, displayName: displayName, text: text) 
    messages.append(message) 
} 

然後我設置顯示名是位置:

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 
      let locationId = snapshot.value!["location"] as! String 

      // 4 
      // self.addMessage(id, text: locationId.lowercaseString + ": \n" + text) 
      self.addMessage(id, text: text, displayName: locationId) 


      // 5 
      self.finishReceivingMessage() 

      Answers.logCustomEventWithName("Visited RoastChat", customAttributes: nil) 

     } 
    } 

然後我用內置的attributedTextForCellBottomLabelAtIndexPath設置我的底部標籤爲用戶的位置:

override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! { 

    let message = messages[indexPath.item] 

    if message.senderId == senderId { 
     return nil 
    } else { 
     return NSAttributedString(string: message.senderDisplayName) 
    } 

} 

它的工作原理完善!這就是編程的全部內容,解決問題。

感謝大家的幫助,並指引我朝着正確的方向前進。

1

99%,當你檢索某些數據來自互聯網 - 就像這裏一樣,您會觀察Firebase事件以獲取其他用戶發送的位置 - 代碼以異步方式工作。

這意味着,當您檢索位置時觸發的閉包將獨立於其餘的attributesTextForCellBottomLabelAtIndexPath函數進行調用。

因此,當你真正獲得位置的時候,函數已經返回了用空的locationId創建的NSAttributedString;爲什麼空?因爲你已經將其初始值設置爲「」。 調用messagesQuery時傳遞的閉包queryQuery.observeEventType在本地捕獲locationId屬性,因此您可以將它設置爲從API檢索的內容並且打印工作正常。但是在流程到達閉包代碼的末尾之後,這個值從內存中消失並且無處使用。

您需要做的事情是讓您的應用程序工作,即在之後更新單元格的底部標籤,您將獲得locationId。你可以用幾種不同的方式去解決它。我會建議兩個類似的。

如果單元能夠在Firebase事件中觀察並更新自己,最簡單也是最直接的方法。

func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) { 
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(YourCellClass.identifier, forIndexPath: indexPath) as! YourCellClass 

    cell.messagesQuery = messagesRef 

    return cell 
} 

和你的代碼中,你將有類似的東西:

class YourCellClass: UICollectionViewCell { 

    static let identifier = String(YourCellClass) 

    var messagesQuery: FIRDatabaseReference { 
     didSet { 
      messagesQuery.observeEventType(.ChildAdded) { [weak self] snapshot in 
       guard let `self` = self, value = snapshot.value as? [String: AnyObject], locationId = value["location"] as? String else { return } 
       dispatch_async(dispatch_get_main_queue()) { 
        self.bottomLabel.attributedText = NSAttributedText(string: locationId) 
       } 
      } 
     } 
    } 

    // all your other cell code goes here 
} 

一個不同的,更好的選擇是使用MVVM和細胞的視圖模型將負責獲取位置,細胞會觀察它的視圖模型並更新自身,但我認爲現在對你來說有點太多了);

PS請記住,我編寫了這個代碼,因此我無法保證100%能馬上編譯。

+0

謝謝!這非常詳細。我只有一個問題,我的單元代碼是什麼意思?你可能看過我包含的代碼,但是我的程序中會包含哪一個類,因爲我很困惑它會是哪一個......再次感謝! –

+0

好的,我忽略了你正在使用某種JSQ集合視圖而不是你自己的事實。在這種情況下,如果可以輕鬆子類化JSQMessagesCollectionViewCell,則可以創建自己的子類,該子類實現獲取位置並將其用於現在使用的位置。有時候,當你繼承了基類的實現時,你必須手動重新實現,但我認爲這不是這種情況。 –

+0

如果您無法創建工作子類,則可以創建一個能夠獲取位置的對象,並在完成閉包中,您的代碼將通過cellForRowAtIndexPath獲取適當的單元格,並將其設置爲底部標籤。這個對象將是你的視圖控制器的一個屬性,你將在viewDidLoad中實例化它。或者,您可以存儲位置ID數組,並將其寫入閉包中,並在collectionView上觸發reloadData(或更新單個indexPath)。爲底部標籤返回適當文本的委託方法將從該數組中獲取它的值。 –

相關問題