Add profile picture to chat with JSQMessagesViewController

2

I'm experimenting with JSQMessagesViewController and Firebase. I have managed to save download Firebase content and save them via chat. Until now I have had problems with the profile image. I am declaring the following two variables:

var incomingAvatar: JSQMessagesAvatarImage!
var outgoingAvatar: JSQMessagesAvatarImage!

In my ViewDidAppear I am working with the following code:

      messageRef.observeEventType(.ChildAdded) { (snapshot: FIRDataSnapshot!) in
        let id = snapshot.value!["senderId"] as! String
        let text = snapshot.value!["text"] as! String
        let image = snapshot.value!["imageURL"] as! String
        let displayName = snapshot.value!["senderDisplayName"] as! String

        self.imageUrl = NSURL(string: image)
        let message = JSQMessage(senderId: id, displayName: displayName, text: text)
        self.messages.append(message)
        self.finishReceivingMessage()


    }

Where I download the link of the profile image and convert it to NSURL

later

override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
    /////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////



    let message = messages[indexPath.item]
     if message.senderId == senderId {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            let data = NSData(contentsOfURL: self.imageUrl!)
            dispatch_async(dispatch_get_main_queue(), {
                self.incomingAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage( UIImage(data: data!), diameter: 250)
            });
        }

                return self.incomingAvatar
    }
     else{

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            let data = NSData(contentsOfURL: self.imageUrl!)
            dispatch_async(dispatch_get_main_queue(), {
                self.outgoingAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage( UIImage(data: data!), diameter: 250)
            });
        }



       // self.outgoingAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(named: "defaultImageCell")!, diameter: 150)
        return self.outgoingAvatar
    }




    /////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////


}

Apparently I'm not understanding the management of the library well. When running the program I see the profile image of the user who writes in all the profile images of the chat and when another user writes something all the images change again.

I appreciate any comments in advance.

    
asked by Víctor Gonzal 01.06.2016 в 01:10
source

1 answer

1

Well, after a lot of documentation I managed to make it work.

I leave the whole code, although it needs to be polished a bit, I hope that this type of response does not make me negative. XD.

class ChatComents: JSQMessagesViewController {
var user: FIRUser?
var messages = [JSQMessage]()
var messagesStack = [JSQMessage]()
var avatars = Dictionary<String, AnyObject>()
var avatarsTapImage = Dictionary<String, UIImage>()
var avatarsUrl = Dictionary<String, String>()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
var senderImageUrl: String!
var batchMessages = true
var messagesBlockCant: UInt = 25
var numeroDeComentarios: Int?
var ref: FIRDatabaseReference!
var imageRemoteURL: String?
//var currentUsername: String?
var messageRef: FIRDatabaseReference!
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
var currentUserAvatar = UIImageView()

// *** STEP 1: STORE FIREBASE REFERENCES
var messagesRef: FIRDatabaseReference!



private func setupBubbles() {

    let factory = JSQMessagesBubbleImageFactory()
    outgoingBubbleImageView = factory.outgoingMessagesBubbleImageWithColor(
        UIColor.jsq_messageBubbleBlueColor())
    incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
        UIColor.jsq_messageBubbleGreenColor()) //jsq_messageBubbleLightGrayColor
}
func convertDateFormater(date: String) -> String {
    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z" // 2016-06-20 20:32:40 +0000
    dateFormatter.timeZone = NSTimeZone(name: "UTC")

    guard let date = dateFormatter.dateFromString(date) else {
        assert(false, "no date from string")
        return ""
    }

    dateFormatter.dateFormat = "d MMM yyyy HH:mm"
    dateFormatter.timeZone = NSTimeZone(name: "UTC")
    let timeStamp = dateFormatter.stringFromDate(date)
    print("fecha reformateada vale: ", timeStamp)
    return timeStamp
}


func setupFirebase() {
    // *** STEP 2: SETUP FIREBASE


    // *** STEP 4: RECEIVE MESSAGES FROM FIREBASE (limited to latest 25
    messageRef.queryLimitedToLast(messagesBlockCant).observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot) in
        let text = snapshot.value!["text"] as! String
        let senderId = snapshot.value!["senderId"] as! String
        let imageUrl = snapshot.value!["imageURL"] as! String
        let senderDisplayName = snapshot.value!["senderDisplayName"] as! String
        let fecha = snapshot.value!["fecha"] as! String
        print("fecha sin formato es: ", fecha) //  ....
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" //"yyyy-MM-dd HH:mm:ss Z" // 2016-06-20 20:32:40 +0000
        let date = dateFormatter.dateFromString(fecha)

        let dateFormatter24Hrs = NSDateFormatter()
        dateFormatter24Hrs.dateFormat = "yyyy-MM-dd hh:mm:ss Z" // 2016-06-20 2311:16:48 p.m. +0000
        //let fechita = dateFormatter.dateFromString(fecha)
        print("La fecha final es: ", date, "del texto: ", text)


        let message =  JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text)
        self.avatarsUrl.updateValue(imageUrl, forKey: senderId)
        print("avatarsUrl vale: ", self.avatarsUrl)
        self.messages.append(message)
        self.finishReceivingMessage()
    })
}

func sendMessage(text: String!, senderId: String!) {
    // *** STEP 3: ADD A MESSAGE TO FIREBASE
    let components = calendar.components([.Day , .Month , .Year], fromDate: date)

    let year =  components.year
    let month = components.month
    let day = components.day
    let fechaComentario = String(String(day) + "/" + String(month) + "/" + String(year))
    print("Fecha del comentario: ", fechaComentario)
    messageRef.childByAutoId().setValue([
        "text": text, //
        "votes": 0,
        "senderDisplayName": senderDisplayName,
        "senderId": senderId, //
        "nombreEntidad": Variable.currentObject,
        "imageURL": senderImageUrl, //
        "fecha": fechaComentario //
        ])
}
func setupAvatarImage(name: String, imageUrl: String?, incoming: Bool, initials: String) {
    print("el name vale: ", name)
    print("el initial vale: ", initials)
    if let stringUrl = imageUrl {
        if let url = NSURL(string: stringUrl) {
            if let data = NSData(contentsOfURL: url) {
                let image = UIImage(data: data)
                let diameter = incoming ? UInt(collectionView!.collectionViewLayout.incomingAvatarViewSize.width) : UInt(collectionView!.collectionViewLayout.outgoingAvatarViewSize.width)
                print("diametro es: ", diameter)

                let avatarImage = JSQMessagesAvatarImageFactory.avatarImageWithImage(image, diameter: diameter)

                avatars[name] = avatarImage
                avatarsTapImage[name] = image

                return
            }
        }
    }
    setupAvatarColor(name, incoming: incoming, initials: initials)
}

func setupAvatarColor(name: String, incoming: Bool, initials: String) {
    let diameter = incoming ? UInt(collectionView!.collectionViewLayout.incomingAvatarViewSize.width) : UInt(collectionView!.collectionViewLayout.outgoingAvatarViewSize.width)

    let rgbValue = name.hash
    let r = CGFloat(Float((rgbValue & 0xFF0000) >> 16)/255.0)
    let g = CGFloat(Float((rgbValue & 0xFF00) >> 8)/255.0)
    let b = CGFloat(Float(rgbValue & 0xFF)/255.0)
    let color = UIColor(red: r, green: g, blue: b, alpha: 0.5)

    let nameLength = initials.characters.count

    let initialsName : String? = initials.substringToIndex(senderId.startIndex.advancedBy(min(3, nameLength)))
    print("INICIALES: ", initialsName)



    let userImage = JSQMessagesAvatarImageFactory.avatarImageWithUserInitials(initialsName, backgroundColor: color, textColor: UIColor.blackColor(), font: UIFont.systemFontOfSize(CGFloat(13)), diameter: diameter)


    avatars[name] = userImage
    avatarsTapImage[name] = userImage.avatarHighlightedImage

}

func loadMore() {
    print("Load earlier messages!")
    //setupFirebase()
    self.collectionView.collectionViewLayout.springinessEnabled = false


    messagesBlockCant += 25
            var contador = 1
    // *** STEP 4: RECEIVE MESSAGES FROM FIREBASE (limited to latest 25
    messageRef.queryLimitedToLast(messagesBlockCant).observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot) in
        self.collectionView.infiniteScrollingView.startAnimating()
        let diferencia = self.numeroDeComentarios! -  self.messages.count
        if  (diferencia != 0) // (self.numeroDeComentarios! >= self.messages.count) //|| (diferencia != 0)
        {
            //self.collectionView.showsInfiniteScrolling = false
            let oldBottomOffset = self.collectionView.contentSize.height - self.collectionView.contentOffset.y

            //print("Este mensaje no deberia aparecer si diferencia es 0: ", diferencia)
            let text = snapshot.value!["text"] as! String
            let senderId = snapshot.value!["senderId"] as! String
            let imageUrl = snapshot.value!["imageURL"] as! String
            let senderDisplayName = snapshot.value!["senderDisplayName"] as! String
            let fecha = snapshot.value!["fecha"] as! String
            let dateFormatter = NSDateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" //"yyyy-MM-dd HH:mm:ss Z" // 2016-06-20 20:32:40 +0000
            let date = dateFormatter.dateFromString(fecha)

            let message =  JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text)


            self.avatarsUrl.updateValue(imageUrl, forKey: senderId)

            if diferencia > 24
            {
                if contador <= 25 {
                    self.messagesStack.insert(message, atIndex: 0)
                     print("La fecha final es: ", date, "del texto: ", text)
                    //print("contador: ", contador)
                }
            }
            else
            {
                if contador <= diferencia+1
                {
                    self.messagesStack.insert(message, atIndex: 0)
                    print("self.messagesStack contiene: ", text)
                    print("text agregado cuando ya no es multiplo de 25: ", diferencia)
                    print("contador diferente a 25: ", contador)
                }
            }

            contador += 1

            if contador == 26{
                if diferencia > 24
                {
                    for limiteMax in 0...24{
                        //print("limiteMax vale: ", limiteMax)
                        //print("messagesStack.count vale: ", self.messagesStack.count)
                        let traspasoValores = self.messagesStack[limiteMax]
                        self.messages.insert(traspasoValores, atIndex: 0)
                        self.finishReceivingMessage()
                    }
                } else{

                    if diferencia != 0 {
                        for limiteMax in 1...diferencia {
                            let traspasoValores = self.messagesStack[limiteMax]
                            self.messages.insert(traspasoValores, atIndex: 0)
                            self.finishReceivingMessage()
                        }
                    }
                }

            }

            self.finishReceivingMessageAnimated(false)
            self.collectionView.layoutIfNeeded()
            self.collectionView.contentOffset = CGPointMake(0, self.collectionView.contentSize.height - oldBottomOffset)


            self.collectionView.infiniteScrollingView.stopAnimating()

            self.collectionView.collectionViewLayout.springinessEnabled = false


        }
        else{
            print("No hay mas comentarios que mostrar")
            self.collectionView.infiniteScrollingView.stopAnimating()

            self.collectionView.collectionViewLayout.springinessEnabled = false
           self.collectionView.showsInfiniteScrolling = false
        }
        self.collectionView.infiniteScrollingView.stopAnimating()


    })


}




override func viewDidLoad() {
    super.viewDidLoad()
    //inputToolbar!.contentView!.leftBarButtonItem = nil
    self.inputToolbar.removeFromSuperview()
    //automaticallyScrollsToMostRecentMessage = true
    self.showLoadEarlierMessagesHeader = true
    self.collectionView.loadEarlierMessagesHeaderTextColor = UIColor.blackColor()


                  // Infinite scrolling


    //navigationController?.navigationBar.topItem?.title = "Atras"
    setupBubbles()
    senderId = (senderId != nil) ? senderId : "Anonymous"
    let profileImageUrl = imageRemoteURL
    if let urlString = profileImageUrl {
        setupAvatarImage(senderId, imageUrl: urlString as String, incoming: false, initials: senderDisplayName)
        senderImageUrl = urlString as String
    } else {
        setupAvatarColor(senderId, incoming: false, initials: senderDisplayName)
        senderImageUrl = ""
    }

    setupFirebase()

       }

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
        //self.collectionView.triggerPullToRefresh()
        Variable.refComment.observeEventType(FIRDataEventType.Value, withBlock: { snapshot in

        let numeroDeComents = snapshot.childrenCount
        self.numeroDeComentarios = Int(numeroDeComents)
        print("y el numero de comentarios en viewDidAppear es: ", self.numeroDeComentarios!)
        }, withCancelBlock: { error in
            print(error.description)
    })

}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
}

override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!,
                                 senderDisplayName: String!, date: NSDate!) {

    let components = calendar.components([.Day , .Month , .Year], fromDate: date)
    let year =  components.year
    let month = components.month
    let day = components.day
    let fechaComentario = String(String(day) + "/" + String(month) + "/" + String(year))
    print("Fecha del comentario: ", fechaComentario)
    let fechaComment = NSDate()
    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    let convertedDate = dateFormatter.stringFromDate(fechaComment)
    print("convertedDate vale: ", convertedDate)
    let messageItem = [
        "text": text, //
        "votes": 0,
        "senderDisplayName": senderDisplayName,
        "senderId": senderId, //
        "nombreEntidad": Variable.currentObject,
        "imageURL": imageRemoteURL!, //
        "fecha": String(convertedDate) //
    ]
    messageRef.childByAutoId().setValue(messageItem)

    // 4
    JSQSystemSoundPlayer.jsq_playMessageSentSound()

    // 5
    finishSendingMessage()
}


func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, date: NSDate!) {
    JSQSystemSoundPlayer.jsq_playMessageSentSound()

}

override func didPressAccessoryButton(sender: UIButton!) {
    print("Camera pressed!")
}

override func collectionView(collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAtIndexPath indexPath: NSIndexPath!) {
    print("Tapped message bubble!")
}

func dismissFullscreenImage(sender: UITapGestureRecognizer) {
    sender.view?.removeFromSuperview()
}

override func collectionView(collectionView: JSQMessagesCollectionView!, didTapAvatarImageView avatarImageView: UIImageView!, atIndexPath indexPath: NSIndexPath!) {
    print("Tapped avatar!")
    let message = messages[indexPath.row]
    Variable.popupUsername = message.senderDisplayName
    Variable.popupImageProfile = avatarsTapImage[message.senderId]!
    Variable.popupImageProfileUrl = avatarsUrl[message.senderId]
    Variable.popupSenderId = message.senderId
    //Variable.popupLoveUserCant =
    //Variable.popupCommentUserCant =

    PopupController
        .create(self)
        .show(ProfileResumeVC.instance())

}

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
    return .None

}

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 {
        return outgoingBubbleImageView
    } else { // 3
        return incomingBubbleImageView
    }

}

override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellTopLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
        let message = self.messages[indexPath.item]
        return JSQMessagesTimestampFormatter.sharedFormatter().attributedTimestampForDate(message.date)
}

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

}

override func collectionView(collectionView: JSQMessagesCollectionView!, header headerView: JSQMessagesLoadEarlierHeaderView!, didTapLoadEarlierMessagesButton sender: UIButton!) {
    print("Load earlier messages!")
           //setupFirebase()


    showLoadEarlierMessagesHeader = true

    self.collectionView.collectionViewLayout.springinessEnabled = false


    messagesBlockCant += 25
    var contador = 1
    // *** CONTANDO EL NUMERO DE COMENTARIOS




    // *** STEP 4: RECEIVE MESSAGES FROM FIREBASE (limited to latest 25
    messageRef.queryLimitedToLast(messagesBlockCant).observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot) in
        let diferencia = self.numeroDeComentarios! -  self.messages.count
        if  (diferencia != 0) // (self.numeroDeComentarios! >= self.messages.count) //|| (diferencia != 0)
        {
            let oldBottomOffset = self.collectionView.contentSize.height - self.collectionView.contentOffset.y


            let text = snapshot.value!["text"] as! String
            let senderId = snapshot.value!["senderId"] as! String
            let imageUrl = snapshot.value!["imageURL"] as! String
            let senderDisplayName = snapshot.value!["senderDisplayName"] as! String
            let fecha = snapshot.value!["fecha"] as! String

            let dateFormatter = NSDateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" //"yyyy-MM-dd HH:mm:ss Z" // 2016-06-20 20:32:40 +0000
            let date = dateFormatter.dateFromString(fecha)

            let message =  JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text)


            self.avatarsUrl.updateValue(imageUrl, forKey: senderId)
            if diferencia > 24
            {
                if contador <= 25 {
                    self.messagesStack.insert(message, atIndex: 0)
                    print("La fecha final es: ", date, "del texto: ", text)
                    }
            }
            else
            {
                if contador <= diferencia+1
                {
                    self.messagesStack.insert(message, atIndex: 0)
                    print("self.messagesStack contiene: ", text)
                    print("text agregado cuando ya no es multiplo de 25: ", diferencia)
                    print("contador diferente a 25: ", contador)
                }
            }

            contador += 1

            if contador == 26{
                if diferencia > 24
                {
                    for limiteMax in 0...24{
                        //print("limiteMax vale: ", limiteMax)
                        //print("messagesStack.count vale: ", self.messagesStack.count)
                        let traspasoValores = self.messagesStack[limiteMax]
                        self.messages.insert(traspasoValores, atIndex: 0)
                        self.finishReceivingMessage()
                    }
                } else{
                    print("No es exacto, debemos hacer algo aqui: ", diferencia)
                    if diferencia != 0 {
                        for limiteMax in 1...diferencia {
                            print("limiteMax vale en xxx: ", limiteMax)
                            print("messagesStack.count vale en xxx: ", self.messagesStack.count)
                            let traspasoValores = self.messagesStack[limiteMax]
                            self.messages.insert(traspasoValores, atIndex: 0)
                            self.finishReceivingMessage()
                        }
                    }
                }

            }

            self.finishReceivingMessageAnimated(false)
            self.collectionView.layoutIfNeeded()
            self.collectionView.contentOffset = CGPointMake(0, self.collectionView.contentSize.height - oldBottomOffset)


            self.collectionView.collectionViewLayout.springinessEnabled = false
                      }
        else{
            print("No hay mas comentarios que mostrar")
                            self.collectionView.collectionViewLayout.springinessEnabled = false
            self.showLoadEarlierMessagesHeader = false
        }
                })





}



override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {

    let message = messages[indexPath.item]
    var imageAvatar : JSQMessageAvatarImageDataSource?
    for senderReferenceImage in avatars.keys where senderReferenceImage == message.senderId
    {

            imageAvatar = avatars[senderReferenceImage] as? JSQMessageAvatarImageDataSource
           // print("Hola desde el primer Ford XD")
            return imageAvatar
    }

    for senderReferenceUrl in avatarsUrl.keys where senderReferenceUrl == message.senderId
    {

        print("message.senderId! vale: ", message.senderId!)
        print("avatarsUrl[senderReferenceUrl] vale: ", avatarsUrl[senderReferenceUrl]!)
        setupAvatarImage(message.senderId!,  imageUrl: avatarsUrl[senderReferenceUrl], incoming: true, initials: message.senderDisplayName)
        imageAvatar = avatars[senderReferenceUrl] as? JSQMessageAvatarImageDataSource
        return imageAvatar

    }
    return JSQMessagesAvatarImage.avatarWithImage(self.currentUserAvatar.image)

}



override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return messages.count
}

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.blackColor()
    } else {
        cell.textView!.textColor = UIColor.blackColor()
    }

    let attributes : [NSObject:AnyObject] = [NSForegroundColorAttributeName:cell.textView!.textColor!, NSUnderlineStyleAttributeName: 1]
    cell.textView!.linkTextAttributes = attributes as AnyObject as! [String : AnyObject]

    return cell
}


// View  usernames above bubbles
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForMessageBubbleTopLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
    let message = messages[indexPath.item];

    // Sent by me, skip
    if message.senderId! == senderId {
        return nil
    }

    // Same as previous sender, skip
    if indexPath.item > 0 {
        let previousMessage = messages[indexPath.item - 1];
        if previousMessage.senderId == message.senderId {
            return NSAttributedString(string:message.senderDisplayName!)
        }
    }

    return NSAttributedString(string:message.senderDisplayName!)
}

override func collectionView(collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
    let message = messages[indexPath.item]

    // Sent by me, skip
    if message.senderId == senderId {
        return CGFloat(0.0);
    }

    // Same as previous sender, skip
    if indexPath.item > 0 {
        let previousMessage = messages[indexPath.item - 1];
        if previousMessage.senderId == message.senderId {
            return CGFloat(0.0);
        }
    }

    return kJSQMessagesCollectionViewCellLabelHeightDefault
}

}

    
answered by 10.07.2016 в 19:02