DBoyer
DBoyer

Reputation: 3122

Creating a Messaging Screen in iOS

I am currently working on an iOS app that has messaging as its core functionality. Messaging screens can get pretty complicated, especially trying to make them up to todays standards. I am wondering what solutions developers have been using to create a messaging screen. Is there a good library to start with (I have checked out JSQMessagesViewController and pretty much every other CocoaPod for messaging) or should I just start from scratch?

The messaging screen I want is not overly complicated and I should be able to accomplish it with a table view, a couple custom cells, and some constraints. I am struggling to determine if I should waste my time customizing something when I could just make it myself.

What should I do, customize an existing library or just do it from scratch?

Any tips?

Upvotes: 4

Views: 1892

Answers (1)

digitalHound
digitalHound

Reputation: 4444

I realize this question has been around for awhile, but since it doesn't have an accepted answer yet I'll submit my solution. The part I'm showing below is for displaying the messages and does not include the reply widget. This should help get people started in a direction that did what I wanted it to do.

I did this using a tableview, a custom cell containing a UITextView property (but did not add the textview in the storyboard, it is instantiated in code), and a table data array containing instances of my SDMessage class.

Here is the contents of the SDMessage class interface file:

@interface SDMessage : NSObject

@property (nonatomic, strong) NSNumber *message_id;
@property (nonatomic, strong) NSString *message_sender;
@property (nonatomic, strong) NSString *message_content;
@property (nonatomic, strong) NSString *their_id;
@property (nonatomic, strong) NSString *timestamp;
@property (nonatomic, strong) NSDate *dateTime;
@property (nonatomic, strong) NSString *prettyTimestamp;
@property (nonatomic, strong) NSNumber *seen;


@end

My tableview delegate methods are as follows:

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.tableData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"MsgDetailCell";
    MsgDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];


    if (!cell) {
        cell = [[MsgDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.frame = CGRectMake(2, 2, tableView.frame.size.width - 4, 30);
    }

    // Since we are creating a new UITextfield with every load, set any existing UITextfield instance to nil
    [cell.message removeFromSuperview];
    cell.message = nil;


    // get the data for this cell (returns self.tableData[indexPath.row])
    SDMessage *cellData = [self cellDataForRowAtIndexPath:indexPath];

    // get sender id to configure appearance (if we're sending a message it will appear 
    // on the right side, if we received a message the message will be on the left side of the screen)
    long long   theirId = [cellData.message_sender longLongValue];

    // we have to do this in code so we can set the frame once and avoid cut off text
    cell.message = [[UITextView alloc] init];

    // set cell text
    cell.message.text = cellData.message_content;

    // set font and text size (I'm using custom colors here defined in a category)
    [cell.message setFont:[UIFont fontWithName:kMessageFont size:kMessageFontSize]];
    [cell.message setTextColor:[UIColor gh_messageTextColor]];

    // our messages will be a different color than their messages
    UIColor *msgColor = (theirId == _myUid) ? [UIColor gh_myMessageBgColor] : [UIColor gh_theirMessageBgColor];

    // I want the cell background to be invisible, and only have the 
    // message background be colored
    cell.backgroundColor = [UIColor clearColor];
    cell.message.backgroundColor = msgColor;
    cell.message.layer.cornerRadius = 10.0;

    // make the textview fit the content (kMessageWidth = 220, kMessageBuffer = 15 ... buffer is amount of space from edge of cell)
    CGSize newSize = [cell.message sizeThatFits:CGSizeMake(kMessageWidth, MAXFLOAT)];
    CGRect newFrame;

    // since we are placing the message bubbles justified right or left on the screen
    // we determine if this is our message or someone else's message and set the cell
    // origin accordingly... (if the sender of this message (e.g. theirId) is us, then take the width
    // of the cell, subtract our message width and our buffer and set that x position as our origin.
    // this will position messages we send on the right side of the cell, otherwise the other party
    // in our conversation sent the message, so our x position will just be whatever the buffer is. (in this case 15)
    float originX = (theirId == _myUid) ? cell.frame.size.width - kMessageWidth - kMessageBuffer : kMessageBuffer;

    // set our origin at our calculated x-point, and y position of 10
    newFrame.origin = CGPointMake(originX, 10);

    // set our message width and newly calculated height
    newFrame.size = CGSizeMake(fmaxf(newSize.width, kMessageWidth), newSize.height);

    // set the frame of our textview and disable scrolling of the textview
    cell.message.frame = newFrame;
    cell.message.scrollEnabled  = NO;
    cell.userInteractionEnabled = NO;

    // add our textview to our cell
    [cell addSubview:cell.message];

    return cell;
}



-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // we need to make sure our cell is tall enough so we don't cut off our message bubble
    MsgDetailCell *cell = (MsgDetailCell*)[self tableView:self.tableview cellForRowAtIndexPath:indexPath];

    // get the size of the message for this cell
    CGSize newSize = [cell.message sizeThatFits:CGSizeMake(kMessageWidth, MAXFLOAT)];

    // get the height of the bubble and add a little buffer of 20
    CGFloat textHeight  = newSize.height + 20;

    // don't make our cell any smaller than 60
    textHeight          = (textHeight < 60) ? 60 : textHeight;

    return textHeight;
}


-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    SLog(@"didSelectRow: %ld", (long)indexPath.row);
}


- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    return 0.01;
}

-(UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    return [UIView new];
}

This gives me a message screen tableview that looks like this: Messaging Screen Image

Upvotes: 2

Related Questions