【iOS】LINE風チャットUIを実装〜スレッド投稿編〜

前回の<a href="http://qiita.com/Kaiketch/items/1d0cc3f7bc3aa4d4161a">メッセージ入力欄編</a>を投稿してからそのままになっていたので、スレッド投稿編も残しておきます。
投稿するメッセージ文字列によって、メッセージLabelのサイズとセルの高さを動的に変化させる方法です。

デザイン超適当ですができあがりはこんな感じです。
※赤が自分の投稿で青が自分以外の投稿。

![s_写真 1.PNG](https://qiita-image-store.s3.amazonaws.com/0/53088/5c5ea7b9-5bd5-239d-439c-aed597ae6606.png)  ![s_写真 2.PNG](https://qiita-image-store.s3.amazonaws.com/0/53088/cb85257d-ff54-d310-275c-b5598f08cdb0.png)  ![s_写真 3.PNG](https://qiita-image-store.s3.amazonaws.com/0/53088/5a4ea848-1378-9f65-fdc8-0e8dcce51f66.png)



## メッセージクラス

まずはメッセージ情報をオブジェクトとして扱うためのクラスを作り、userId、userName、messageプロパティとコンストラクタを定義しておきます。
本来であればアイコン画像のURL等も持たせるかと思います。

```objc:Message.h
@interface Message : NSObject

@property (strong, nonatomic) NSString *userId;
@property (strong, nonatomic) NSString *userName;
@property (strong, nonatomic) NSString *message;

+ (Message *) messageWithUserId:(NSString *)userId userName:(NSString *)userName message:(NSString *)message;
@end
```

```objc:Message.m

@implementation Message

+ (Message *) messageWithUserId:(NSString *)userId userName:(NSString *)userName message:(NSString *)message{
    
    Message *msg = [[Message alloc]init];
    msg.userId = userId;
    msg.userName = userName;
    msg.message = message;
    
    return msg;
}
@end

```

## カスタムセル×２

今回はカスタムセルクラスのコードでメッセージLabel等のViewを作っていきます。
ヘッダクラスには、メッセージLable等のView、メッセージオブジェクト（前述のメッセージクラス）、メッセージLabelの高さと幅を格納するプロパティ（CGFloat messageLabelHeight,messageLabelWidth）を定義しておきます。
また、自分以外のメッセージ用のMessageCellと、自分のメッセージ用のMyMessageCellをそれぞれ作ります。

メッセージLabelは高さ（messageLabelHeight）と幅（messageLabelWidth）が動的に設定されるようにしておきます。

```objc:MessageCell.m

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        
        _imageViewIcon = [[UIImageView alloc]init];
        [self addSubview:_imageViewIcon];
        
        _labelUserName = [[UILabel alloc] init];
        _labelUserName.font = [UIFont systemFontOfSize:15];
        [self addSubview:_labelUserName];
        
        _labelMessage = [[UILabel alloc]init];
        _labelMessage.font = [UIFont systemFontOfSize:15];
        _labelMessage.numberOfLines = 0;
        _labelMessage.lineBreakMode = NSLineBreakByCharWrapping;
        [self addSubview:_labelMessage];
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    //ユーザーアイコン
    _imageViewIcon.frame = CGRectMake(10, 10, 40, 40);
    _imageViewIcon.backgroundColor = [UIColor grayColor];
    
    //ユーザー名
    _labelUserName.frame = CGRectMake(60, 10, 180, 15);
    _labelUserName.text = _message.userName;
    
    //メッセージ部分
    _labelMessage.frame = CGRectMake(60, 30, _messageLabelWidth, _messageLabelHeight);
    _labelMessage.backgroundColor = [UIColor colorWithRed:120/255.0 green:120/255.0 blue:255/255.0 alpha:1.0];
    _labelMessage.text = _message.message;
}
```

MyMessageCellの方では、アイコンとユーザー名を省きメッセージ欄は右寄せになるようにして、チャットUIっぽくしておきます。

```objc:MyMessageCell.m

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    //セルの幅マイナスメッセージラベルの幅マイナスpadding10に配置
    _labelMessage.frame = CGRectMake(self.frame.size.width -_messageLabelWidth -10, 30, _messageLabelWidth, _messageLabelHeight);
    _labelMessage.backgroundColor = [UIColor colorWithRed:255/255.0 green:120/255.0 blue:120/255.0 alpha:1.0];
    _labelMessage.text = _message.message;
}

```


## テーブルビュー

### UILabelのサイズを算出するメソッドの作成

まず、メッセージ文字列とフォントと高さと幅の最大値を渡して、それが納まるUILabelのサイズを返すメソッドを作っておきます。
サイズの取得にはNSAttributedString#boundingRectWithSizeを使っています。

```objc:ChatViewController.m

- (CGSize)getLabelSize:(NSString *)text font:(UIFont *)font width:(float)width height:(float)height{
    
    CGSize maxSize = CGSizeMake(width, height);
    NSDictionary *attr = @{NSFontAttributeName: font};
    
    CGSize size = [text boundingRectWithSize:maxSize
                                     options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine
                                  attributes:attr
                                     context:nil
                   ].size;
    return size;
}

```

### セルの描画部分

cellForRowAtIndexPathにてUILabelのサイズを算出し、メッセージオブジェクトとあわせてカスタムセルに渡します。
また、自分のidとメッセージオブジェクトのuserIdが一致するかをチェックしcellを切替ます。

```objc:ChatViewController.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    Message* msg = _messageList[indexPath.row];
    
    //メッセージラベルの最大幅を取得（画面の幅マイナスpadding70）
    CGFloat maxMessageLabelWidth = self.view.frame.size.width - 70;
    
    //メッセージラベルの高さを取得
    CGFloat messageLabelHeight = [self getLabelSize:msg.message font:[UIFont systemFontOfSize:15.0] width:maxMessageLabelWidth height:CGFLOAT_MAX].height;
    
    //メッセージラベルの幅を所得。
    CGFloat messageLabelWidth = [self getLabelSize:msg.message font:[UIFont systemFontOfSize:15.0] width:maxMessageLabelWidth height:messageLabelHeight].width;
    
    //自分か自分以外かをチェック
    if([msg.userId isEqualToString:_myId]){
        
        MyMessageCell *cell = (MyMessageCell*)[tableView dequeueReusableCellWithIdentifier:@"MyMessageCell" forIndexPath:indexPath];
        
        cell.message = msg;
        cell.messageLabelHeight = messageLabelHeight;
        cell.messageLabelWidth = messageLabelWidth;
        
        //背景の設定
        cell.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        
        return cell;
        
    } else{
        MessageCell *cell = (MessageCell*)[tableView dequeueReusableCellWithIdentifier:@"MessageCell" forIndexPath:indexPath];
        
        cell.message = msg;
        cell.messageLabelHeight = messageLabelHeight;
        cell.messageLabelWidth = messageLabelWidth;
        
        //背景の設定
        cell.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        
        return cell;
    }
}

```

### セルの高さの設定

heightForRowAtIndexPathにてセルの高さを計算して返します。
説明は割愛しますが、iOS8以上が対象の場合はUITableViewAutomaticDimensionとUITableViewCell#sizeThatFitsを使ってセルの高さを調整しても良いです。

```objc:ChatViewController.m

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        Message* msg = _messageList[indexPath.row];
        
        //メッセージラベルの最大幅を取得（画面の幅マイナスpadding70）
        CGFloat maxMessageLabelWidth = self.view.frame.size.width - 70;
        
        //メッセージラベルの高さを取得
        CGFloat messageLabelHeight = [self getLabelSize:msg.message font:[UIFont systemFontOfSize:15.0] width:maxMessageLabelWidth height:CGFLOAT_MAX].height;
        
        //cellのデフォルトの高さ（ここでは60）よりメッセージの高さ＋padding40の方が大きい場合は、cellをその高さにする。
        CGFloat cellHeight = 60;
        if(messageLabelHeight + 40 > cellHeight) cellHeight = messageLabelHeight + 40;
        
        //セルのサイズを返す
        return cellHeight;
}

```

## おわりに
Swiftじゃなくてごめんなさい。
GitHubにあげておいたので、細かい部分や投稿した時の動作確認をしたい場合はこちらからどうぞ！
https://github.com/Kaiketch/ChatUISample2