仿微信聊天界面实现

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

笔者参考了大神ibiremeiOS 保持界面流畅的技巧中的思想,为每种消息创建一个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
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,148评论 1 32
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,259评论 4 61
  • 一年之计在于春,今日又是立春,想好好总结一下今后的一些安排。 首先每天完成翻译和读书笔记的作业。经过这两周的适应,...
    Reiko丶阅读 112评论 0 0
  • 夜深沉, 夜漫长。 街边的路灯, 一点幽暗的光, 悄悄的投进窗。 老式的, 方型的, 带点花的天花板。 有蜘蛛在织...
    树叶_1531阅读 441评论 12 24
  • 春回大地,乍暖还寒。晨曦微露时起床,为家人煮一碗营养丰富的八宝粥,已成为习惯。温香软糯的热粥足以唤醒沉睡了一夜的胃...
    惠顾星辰阅读 1,107评论 33 25