仿微信聊天界面实现

上篇我们已经封装好了键盘,接下来就来实现信息流界面。很容易看出,信息流界面是一个tableView,里面的每条消息则是一个个cell;因为消息分纯文本、图片、视频、语音、文件等几类,怎么实现这些cell;笔者在网上查看了一些优秀的实现,分别为以下几种:
1、使用CoreText来绘制cell,有VVeboTableView
2、使用Autolayout自动计算高度,有FDTemplateLayoutCell

笔者参考了大神ibireme的iOS 保持界面流畅的技巧中的思想,为每种消息创建一个cellLayout用来计算相关控件的frame,并且这些cellLayout继承于相同的父类LXChatLayout,该父类包含了头像名字发送中背景发送失败等控件的framemessage的值。为每种消息创建一个tableViewCell,这些cell继承于父类LXChatCell。该父类有layout头像名字发送中发送失败cell的背景等属性,还有处理相关事件的功能。通过先获取数据,然后计算相关控件的布局并缓存到内存中,再显示到tableView上,使用YYFPSLabel来检测拖动tableView时的Fps基本在50以上。
talk is cheap,show code

//LXChatLayout.h

@interface LXChatLayout : NSObject {
    UIEdgeInsets rightBubbleInset;  // 右边cell背景的间距
    UIEdgeInsets leftBubbleInset;   // 左边cell背景的间距
    CGFloat hInset;                 // 头像与tableView的间距
    CGFloat marginHead;             // 名字与头像的间距
    CGFloat topInset;               // cell的顶部间距
}

@property (nonatomic, strong) LXMessage *message;
@property (nonatomic, assign) CGRect headRect;
@property (nonatomic, assign) CGRect nameRect;
@property (nonatomic, assign) CGRect indicatorRect;
@property (nonatomic, assign) CGRect bgImgRect;
@property (nonatomic, assign) CGRect failRect;

@property (nonatomic, assign) CGFloat cacheHeight;

+ (instancetype)layoutWithMessage:(LXMessage *)msg;

- (BOOL)isMedia;

#pragma mark - rewrite methods
- (void)computeMineBy:(LXMessage *)message;
- (void)computeOtherBy:(LXMessage *)message;
@end

// LXChatCell.h
typedef void(^LXChatTapContentHandle)(LXChatLayout *layout, CGRect fileRect);
@interface LXChatCell : UITableViewCell

@property (nonatomic, strong) LXChatLayout *layout;

@property (nonatomic, strong) UIImageView *headImgView;
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UIActivityIndicatorView *indicatorView;
@property (nonatomic, strong) UIImageView *failImgView;
@property (nonatomic, strong) UIImageView *bgImgView;
@property (nonatomic, copy) LXChatTapContentHandle tapHandle;

- (void)tapContent:(UITapGestureRecognizer *)gesture;
@end

以上是父类的声明,下面将以纯文本消息为例来实现一个纯文本消息的cell及其cellLayout,其它的消息则参照这种方式即可实现。其关键代码如下

// LXChatTextLayout.h
@interface LXChatTextLayout : LXChatLayout

@property (nonatomic, assign) CGRect textRect;
@property (nonatomic, strong) NSMutableAttributedString *attribute;
@property (nonatomic, strong) YYTextLayout *textLayout;
@end

@implementation LXChatTextLayout

- (void)setMessage:(LXMessage *)message {
    [super setMessage:message];
    // 计算attributeString
    ....
}

- (void)computeMineBy:(LXMessage *)message {
    [super computeMineBy:message];
    ....
    // 计算出文本、背景气泡的frame以及 cell的高度
    _textRect = CGRectMake(bgX + contentInset.left, bgY + insetTop, textWidth, textHeight);
    self.bgImgRect = CGRectMake(bgX, bgY, textWidth + contentInset.left + contentInset.right, bgHeight);
    
    self.cacheHeight += CGRectGetMaxY(self.bgImgRect);
}

- (void)computeOtherBy:(LXMessage *)message {
    [super computeOtherBy:message];
    ....
    // 计算出文本、背景气泡的frame以及 cell的高度
    _textRect = CGRectMake(bgX + contentInset.left, bgY + insetTop, textWidth, textHeight);
    self.bgImgRect = CGRectMake(bgX, bgY, textWidth + contentInset.left + contentInset.right, bgHeight);
    
    self.cacheHeight += CGRectGetMaxY(self.bgImgRect);
}

// LXChatTextCell.h
@interface LXChatTextCell : LXChatCell

@property (nonatomic, strong) YYLabel *contentLabel;
@end

@implementation LXChatTextCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        _contentLabel = [[YYLabel alloc] init];
        //_contentLabel.backgroundColor = [UIColor orangeColor];
        [self.contentView addSubview:_contentLabel];
    }
    return self;
}

- (void)setLayout:(LXChatLayout *)layout {
    [super setLayout:layout];
    
    LXChatTextLayout *tLayout = (LXChatTextLayout *)layout;
    self.contentLabel.textLayout = tLayout.textLayout;
    [CATransaction begin]; // 关闭更改frame的隐式动画
    [CATransaction setDisableActions:true];
    self.contentLabel.frame = tLayout.textRect;
    [CATransaction commit];
}
@end

在这里显示纯文本使用的是YYLabel,是YYText库中的一部分,功能非常强大,也支持异步绘制。在测试的过程中发现,如果快速拖动tableView,更改控件的frame时,会看到有一个动画的过程,这种体验很差;后来查找资料说是因为更改layerframe产生隐式动画导致的,使用以下方法可以关闭该隐式动画。

    [CATransaction begin]; 
    [CATransaction setDisableActions:true];
    // 更改frame
    ....
    [CATransaction commit];

完成后效果如下


chat5.gif

你可能感兴趣的:(仿微信聊天界面实现)