Firebase中的文本检索未在应用中显示

时间:2016-08-03 19:19:01

标签: swift firebase firebase-realtime-database jsqmessagesviewcontroller

我正在开发一个消息传递应用程序,我想显示消息内容下发送消息的位置。我尝试通过将用户的位置数据发送到Firebase,然后尝试检索数据以将其显示为字符串来完成此操作。

获取用户位置正常(我使用CoreLocation这样做)和将数据上传到Firebase实时数据库一样。我将位置与每条消息一起保存:

让itemRef = messageRef.childByAutoId()// 1     让messageItem = [// 2         “文字”:文字,         “senderId”:senderId,         “location”: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))

    }

}

2 个答案:

答案 0 :(得分:2)

99%的时间从互联网上检索某些数据 - 就像这里的情况一样,您观察Firebase事件以获取由其他用户发送的位置 - 代码以异步方式工作。

这意味着在检索位置时触发的闭包是独立于referencedTextForCellBottomLabelAtIndexPath函数的其余部分调用的。

因此,当您实际获得该位置时,该函数已经返回了使用空locationId创建的NSAttributedString;为什么空?因为你已经将它的初始值设置为""。 调用messagesQuery.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,并且单元格的视图模型将负责获取位置,单元格将在其上观察视图模型并更新自身,但我认为现在对你来说有点太多了;)

P.S。请记住,我编写了这个顶级代码,因此我无法100%保证它会立即编译。

答案 1 :(得分:0)

我无法以任何其他方式进行检索。相反,我利用JSQMessagesViewControllers其他内置方法来解决我的问题。该框架有一个名为senderDisplayName的内置变量。由于我正在制作的这个应用程序的性质是匿名的,我不需要使用它,所以我操纵senderDisplay名称来显示用户的位置。

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

然后我将displayName设置为位置:

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

}

它完美无缺!这就是编程的全部内容,解决问题。

感谢所有帮助过我并指出正确方向的人。