6

Its possible change logic of collectionView: attributedTextForCellTopLabelAtIndexPath: delegate method for show date timestamp not by indexPath.item % 4 == 0? how like in SOMessaging day by day? or whatever?

this coding is for displaying timestamp.

- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView
                   layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath
{

    if (indexPath.item % 3 == 0) {
        return kJSQMessagesCollectionViewCellLabelHeightDefault;
    }

    return 0.0f;
}

Current existing logic is displaying same timestamp is duplicated as follow.

enter image description here

PPShein
  • 13,309
  • 42
  • 142
  • 227

3 Answers3

22

Because each JSQMessage object has a date property, you can simply compare the date of each message to the date of the previous message.

[thisMessageDate timeIntervalSinceDate:(NSDate *)previousMessageDate] will give you the difference in seconds. If the difference is greater than, say, a minute (or whatever time interval you desire), then display a timestamp.

This is how I'm doing it:

- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath {
JSQMessage *message = [self.messages objectAtIndex:indexPath.item];

  if (indexPath.item == 0) {
      return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date];
  }

  if (indexPath.item - 1 > 0) {
    JSQMessage *previousMessage = [self.messages objectAtIndex:indexPath.item - 1];

    if ([message.date timeIntervalSinceDate:previousMessage.date] / 60 > 1) {
        return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date];
    }
  }

  return nil;
}

And then just repeat this logic to make sure the timestamps have the correct heights:

- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView
               layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath {

  if (indexPath.item == 0) {
    return kJSQMessagesCollectionViewCellLabelHeightDefault;
  }

  if (indexPath.item - 1 > 0) {
    JSQMessage *previousMessage = [self.messages objectAtIndex:indexPath.item - 1];
    JSQMessage *message = [self.messages objectAtIndex:indexPath.item];

    if ([message.date timeIntervalSinceDate:previousMessage.date] / 60 > 1) {
        return kJSQMessagesCollectionViewCellLabelHeightDefault;
    }
  }

  return 0.0f;
}
cerenali
  • 289
  • 2
  • 4
  • 1
    nice answer. Only thing is that it should be "if (indexPath.item > 0)" instead of "if (indexPath.item - 1 > 0)" in both callbacks. You are missing out on the logic for the second item in relation to the first. – yzucker Apr 05 '16 at 16:06
1

To show timestamp cell only day by day;

With @cerenali's answer, we may have problems for dates which have different days but close times. Like:

msgDate1 = 31/03/2016 23:55
msgDate2 = 01/04/2016 00:07

to handle this, i replaced the logic inside if with:

BOOL checkTime = message.date.year != previousMessage.date.year || message.date.month != previousMessage.date.month || message.date.day != previousMessage.date.day;

and the final code will be:

    JSQMessage *message = [self.messages objectAtIndex:indexPath.item];
    if (indexPath.item == 0) {
        return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date];
    }

    if (indexPath.item - 1 > -1) {
        JSQMessage *previousMessage = [self.messages objectAtIndex:indexPath.item - 1];
        BOOL checkTime = message.date.year != previousMessage.date.year || message.date.month != previousMessage.date.month || message.date.day != previousMessage.date.day;
        if (checkTime) {
            return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date];
        }
    }

Note: I am using DateTools in my project.

alioguzhan
  • 7,657
  • 10
  • 46
  • 67
1

Thanks, @cerenali for the nice answer.

In Swift-3, @cerenali code can be written in:-

     override func collectionView(_ collectionView: JSQMessagesCollectionView, attributedTextForCellTopLabelAt indexPath: IndexPath) -> NSAttributedString? {

                    let message = self.messages[indexPath.item]
                    if indexPath.item == 0 {
                        return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: message.date)
                    }

                    if indexPath.item -  1 > 0{
                        let previousMessage = self.messages[indexPath.item - 1 ]

                        if  ( ( message.date.timeIntervalSince(previousMessage.date) / 60 ) > 1){
                            return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: message.date)
                        }
                    }

                    return nil  
    }

     override func collectionView(_ collectionView: JSQMessagesCollectionView, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout, heightForCellTopLabelAt indexPath: IndexPath) -> CGFloat {

            if indexPath.item == 0 {
                return kJSQMessagesCollectionViewCellLabelHeightDefault
            }

            if indexPath.item -  1 > 0{
                let message = self.messages[indexPath.item]
                let previousMessage = self.messages[indexPath.item - 1 ]

                if  ( ( message.date.timeIntervalSince(previousMessage.date) / 60 ) > 1){
                    return kJSQMessagesCollectionViewCellLabelHeightDefault
                }
            }
            return 0.0
        }
Shrawan
  • 7,128
  • 4
  • 29
  • 40
  • hiii bro @Shrawan have u implemented JSQMessagesViewController i need help in that – Dilip Tiwari Jul 25 '18 at 07:51
  • bro i want to delete messages from chat page self.messages.remove(at: indexPath.item) self.collectionView.reloadData() if i call above in "performAction" it is working but i have to get message id and call the api – Dilip Tiwari Aug 04 '18 at 05:52