0

メッセージングアプリケーションを使用していて、メッセージコンテンツの下にメッセージが送信された場所を表示したいとします。私は、ユーザーのロケーションデータをFirebaseに送信してから、データを取得して文字列として表示しようとしています。Firebaseからのテキストの取得がアプリに表示されない

Firebaseのリアルタイムデータベースにデータをアップロードするのと同じように、ユーザーの所在地を取得することができます(私はCoreLocationを使用しています)。テキスト、 "のSenderID":のSenderID、 「

がitemref要素= messageRef.childByAutoId()// 1 せmessageItem = [// 2 "テキスト" ましょう:私のような各メッセージと一緒に場所を保存します場所 "の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

私は他の方法で検索を行うことができませんでした。代わりに私はJSQMessagesViewControllerを使って、他の方法で作成した問題を解決しました。フレームワークにはsenderDisplayNameという変数が組み込まれています。私が作っているこのアプリの性質は匿名であるので、私はそれを使う必要はなかったので、senderDisplayの名前を操作して代わりにユーザの場所を表示しました。

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

} 

それは動作します:私は、ユーザーの場所として私の下のラベルを設定するattributedTextForCellBottomLabelAtIndexPathに建てられた使用後

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) 

     } 
    } 

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

は、その後、私は場所するのdisplayNameを設定しました完璧!これはすべてのプログラミング、つまり問題解決のためのものです。

正しい方向に私を助けてくれた皆様に感謝します。

1

99%インターネットから - ここの場合のようにFirebaseイベントを観察して、他のユーザによって送信された場所を取得します - コードは非同期で動作します。

つまり、場所を取得したときに発生するクロージャは、残りのattributedTextForCellBottomLabelAtIndexPath関数とは独立して呼び出されます。

実際に場所を取得するまでに、関数は既に空のlocationIdで作成されたNSAttributedStringを返しました。なぜ空ですか?初期値を ""に設定しているためです。 messagesQuery.observeEventTypeを呼び出すとクロージャが渡され、ローカルでlocationIdプロパティが取得されるため、APIから取得したものに設定できるため、印刷が正常に動作します。しかし、フローがクロージャのコードの最後に到達した直後に、この値はメモリから消えてどこでも使用されません。

あなたのアプリケーションを動作させるために必要なことは、の後にセルのボトムラベルを更新することです。あなたはいくつかの異なる方法でそれについて行くことができます。私は2つの同様のものを提案します。

最も簡単で簡単なのは、セルが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

ありがとうございます!これは非常に詳細でした。私はちょうど1つの質問を持っています、私のセルコードはどういう意味ですか?あなたは私が含まれているコードを見ているかもしれませんが、どのクラスが私のプログラムに対応しているのでしょうか?私は混乱しています...もう一度ありがとう! –

+0

私はあなたがJSQコレクションビューを使用していて、あなた自身ではないことを見落としました。この場合、JSQMessagesCollectionViewCellを簡単にサブクラス化することが可能な場合は、場所の取得を実装する独自のサブクラスを作成し、現在使用しているものの代わりに使用することができます。時には、サブクラス化すると、基本クラスの実装の一部が失われているため、手動で再実装する必要がありますが、ここではそうではないと思います。 –

+0

作業サブクラスを作成できない場合は、ロケーションを取得できるオブジェクトを作成できます。完了クロージャでは、コードはcellForRowAtIndexPathを介して適切なセルを取得し、そのボトムラベルを設定します。このオブジェクトはビューコントローラのプロパティで、viewDidLoadでインスタンス化します。別の方法として、位置IDの配列を格納し、クロージャー内に書き込んで、collectionViewでreloadDataをトリガーする(または単一のindexPathを更新する)ことができます。ボトムラベルに適切なテキストを返すデリゲートメソッドは、この配列から値を取得します。 –

関連する問題