使用UITableView纯代码模仿QQ聊天界面

UItableView在开发中使用的频率非常高   所以今天本人通过tableView用纯代码的方式来模仿以下这么个界面

使用UITableView纯代码模仿QQ聊天界面_第1张图片

首先看这个界面的信息是一条一条显示的  所以选择用tabView实现   既然是tableView就需要数据源加载数据   这时创建数据模型XCmessage(注:这里数据模型中的数据是通过plist来加载的)  用来存放数据   数据模型代码如下所示

typedef enum{

    Other = 0,

    me

}userType;

@interface XCmessage : NSObject

/** 时间 */

@property (copy,nonatomicNSString *time;

/** 正文 */

@property (copy,nonatomicNSString *text;

/*

 这里需要定义的属性 虽然是数字 虽然只有两种状态  但是如果定义为BOOL或者NSinteger类型的话  阅读性较差

 所以这里考虑定义成枚举类型  用来保存用户的类型  究竟是自己  还是别人

 */

@property (assign,nonatomicuserType type;

@property (assign,nonatomic,getter=isHidden) BOOL timeHid;

//初始化方法

- (instancetype) initWithDict:(NSDictionary *)dict;

+ (instancetype) messageWithDict:(NSDictionary *)dict;



@implementation XCmessage

- (instancetype) initWithDict:(NSDictionary *)dict{


    self = [super init];

    if(self){

        [self setValuesForKeysWithDictionary:dict];

    }

    return self;

}

+ (instancetype) messageWithDict:(NSDictionary *)dict{

    return [[self alloc]initWithDict:dict];

}

@end


数据模型建立完成之后,发现如果用系统提供的cell满足不了我对界面的需求,所以就要自定义cell,在cell的初始化方法中,添加子控件。而且cell中还应该有一个数据模型,用来给cell的各个控件赋值以及计算控件的frame,如果用XCmessage作为cell的模型,就要将cell中控件的frame计算放在这个模型中,这时,每次显示cell的时候都会计算一遍控件的frame,这样会消耗性能。这样就考虑能不能有什么方法  让我的frame只计算一次就可以了呢?原来想到懒加载中的内容只加载一次就可以了!所以又考虑能不能将cell中控件的frame放到懒加载中计算呢?这样的话frame就只计算一次的,当要显示cell的时候,就像XCmessage提供数据一样,直接给控件的frame提供计算好的frame并赋值就好了。因此,我的方法是,再提供一个XCmessageFrame模型,而且我希望在显示cell的时候,只提供一个XCMessageFrame模型就完成对控件的数据以及frame赋值,所以,这个模型中包含XCmessage模型,用来提供数据,以及计算好的frame,用来提供frame。

代码如下

@class XCmessage;

@interface XCmessageFrame : NSObject

//数据模型

@property (strong,nonatomicXCmessage * message;

/** 时间frame */

@property (assign,nonatomic,readonlyCGRect timeF;

/** 头像frame */

@property (assign,nonatomic,readonlyCGRect iconF;

/** 正文frame */

@property (assign,nonatomic,readonlyCGRect textF;

/** 行高 */

@property (assign,nonatomic,readonlyCGFloat cellHeight;

@end



//重写messageset方法  以便为cell赋值

- (void)setMessage:(XCmessage *)message{

    //将值赋值给 self.message

    _message = message;

    //计算frame

    [self settingFrame];

}


//计算frame方法

- (void) settingFrame{

    //获取屏幕的宽

    CGFloat screenW = [UIScreen mainScreen].bounds.size.width;

   

 //计算时间

    if(self.message.timeHid == NO){

        CGFloat timeX = 0;

        CGFloat timeY = 0;

        CGFloat timeW =screenW;

        CGFloat timeH = 21;

        _timeF = CGRectMake(timeX, timeY, timeW, timeH);

    }

    //计算头像

    CGFloat magin = 10;

    CGFloat iconX = 0;

    CGFloat timeMaxY = CGRectGetMaxY(_timeF);

    CGFloat iconY = timeMaxY + magin;

    CGFloat iconW = 50;

    CGFloat iconH = iconW;

    if(self.message.type == Other){

         iconX = magin;      

        _iconF = CGRectMake(iconX, iconY, iconW, iconH);

    }else{

        iconX = screenW - magin - iconW;

        _iconF = CGRectMake(iconX, iconY, iconW, iconH);

    }

    //计算文本框

    //计算文本框的时候  一般只计算宽  不计算高 

    CGSize textMaxSize = CGSizeMake(180 , MAXFLOAT);

    CGSize textSize = [self sizeWithText:self.message.text font:textFont maxSize:textMaxSize];

        //分为自己发得消息  和别人发得消息两种情况

    //因为是分两种情况  所以先定义一个textX 并初始化为用来分情况储存正文的x

    CGFloat textX = 0;

    CGFloat textY = iconY;    

    //设置按钮的fram

    CGSize btnSize = CGSizeMake(textSize.width + 50, textSize.height + 40);

    if(self.message.type == Other){

    

        textX = CGRectGetMaxX(_iconF) + magin;

    }else{    

        textX = iconX - magin - btnSize.width;     

    }

    _textF = (CGRect){{textX,textY},btnSize};    

    //计算行高

    CGFloat iconMaxY = CGRectGetMaxY(_iconF);

    CGFloat textMaxY = CGRectGetMaxY(_textF);    

    _cellHeight = MAX(iconMaxY, textMaxY) + magin;

}

//计算文字尺寸

- (CGSize) sizeWithText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)size{    

   return  [text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size;

}

@end


数据模型定义完成之后   创建自定的cell

代码如下

@class XCmessageFrame;

@interface XCmassageCell : UITableViewCell

//引入 frame模型作为属性来设置数据

@property (strong,nonatomicXCmessageFrame * messageFrame;

//重写初始化方法

- (instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;

//快速创建cell的类方法

+ (instancetype) cell:(UITableView *)tableView;

@end


//cell中一初始化时就应具备的属性

@interface XCmassageCell ()


/** 时间 */

@property (weak,nonatomicUILabel *timeView;

/** 头像 */

@property (weak,nonatomicUIImageView *iconView;

/** 正文 */

@property (weak,nonatomicUIButton *textView;


@end


@implementation XCmassageCell

+ (instancetype) cell:(UITableView *)tableView{

    //定义重用标示符

    static NSString * ID = @"message"; 

    //判断tableView缓存池中是否有所需cell

    XCmassageCell * cell = [tableView dequeueReusableCellWithIdentifier:ID]; 

    //如果没有 创建cell

    if(cell == nil){

        cell = [[XCmassageCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];

    { 

    //返回cell

    return cell;

}


//重写UItableViewCell的初始化方法  初始化自定义的cell

- (instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{

    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if(self){

        //设置cell的背景

        self.backgroundColor = [UIColor clearColor];

        //初始化控件

        /** 时间 */

        UILabel * timeView = [[UILabel alloc]init];

        [self.contentView addSubview:timeView];

        timeView.textAlignment = NSTextAlignmentCenter;

        timeView.font = textFont;

        self.timeView = timeView;

        /** 头像 */

        UIImageView * iconView = [[UIImageView alloc]init];

        [self.contentView addSubview:iconView];

        _iconView = iconView;

        /** 正文 */

        UIButton * textView = [[UIButton alloc]init];

        [self.contentView addSubview:textView];

        [textView setTitleColor:[UIColor blackColorforState:UIControlStateNormal]; 

        textView.titleLabel.font = textFont;

        textView.titleLabel.numberOfLines = 0;

        _textView = textView;

        //设置内边距

        self.textView.contentEdgeInsets = UIEdgeInsetsMake(2020,2020);    

        self.clipsToBounds = YES;

    }

        return self;

}


//重写messageFrameset方法  cell设置数据

- (void)setMessageFrame:(XCmessageFrame *)messageFrame{

    _messageFrame = messageFrame;

    //设置数据

    [self settingDate];

    //设置frame

    [self settingFrame];

}

//设置数据方法

- (void) settingDate{

   XCmessage * message = self.messageFrame.message;

    //设置时间

    self.timeView.text = message.time;

    //设置头像  这里要设置自己的头像和其他人的头像

    if(message.type == Other){

        self.iconView.image = [UIImage imageNamed:@"Other"];

        //设置聊天气泡 

       UIImage * image = [UIImage imageNamed:@"chat_recive_nor"]; 

      //保证图片拉伸不变形

       UIImage * newimage = [image stretchableImageWithLeftCapWidth:image.size.width*0.5 topCapHeight:image.size.height*0.5];

       [self.textView setBackgroundImage:newimage forState:UIControlStateNormal];

    }else{

        self.iconView.image = [UIImage imageNamed:@"me"];

        UIImage * image = [UIImage imageNamed:@"chat_send_nor"];

        UIImage * newimage = [image stretchableImageWithLeftCapWidth:image.size.width*0.5 topCapHeight:image.size.height*0.5];

        //设置聊天气泡

        [self.textView setBackgroundImage:newimage forState:UIControlStateNormal];

    }

    //设置正文

    [self.textView setTitle:message.text forState:UIControlStateNormal];   }

- (void) settingFrame{

    self.iconView.frame = self.messageFrame.iconF;

    self.timeView.frame = self.messageFrame.timeF;

    self.textView.frame = self.messageFrame.textF;

}

@end


再控制器中实现数据源方法  

代码如下

@interface ViewController () <UITableViewDataSource,UITableViewDelegate>

@property (weaknonatomicIBOutlet UITableView *tableView;

/*

 文本框不应设置在tableView  否则滑动的时候 文本框也会跟着滑动

 所以定义一个transView用来添加文本框  并将transView添加到控制器的View

 */

@property (weaknonatomicIBOutlet UIView *transView;

@property (strong,nonatomicNSMutableArray * messageFrameModle;

@property (weaknonatomicIBOutlet UITextField *inputView;

@end


@implementation ViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    //设置数据

    self.tableView.dataSource = self;  

    self.tableView.delegate = self;

    self.tableView.allowsSelection = NO;

    self.tableView.backgroundColor = [UIColor lightGrayColor];    

    //隐藏cell边线

    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    return self.messageFrameModle.count;

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    XCmassageCell * cell = [XCmassageCell cell:tableView];

    cell.messageFrame = self.messageFrameModle[indexPath.row];

    return cell;

}

//设置多个行高

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

    

    XCmessageFrame * messageFrame = self.messageFrameModle[indexPath.row];

    

    return messageFrame.cellHeight;

}

//隐藏状态栏

-(BOOL)prefersStatusBarHidden{

    return YES;

}


//懒加载

- (NSMutableArray *)messageFrameModle{ 

    if(_messageFrameModle == nil){

        _messageFrameModle = [NSMutableArray array];

        NSString * path = [[NSBundle mainBundle]pathForResource:@"messages.plist" ofType:nil];    

        NSArray * dicArr = [NSArray arrayWithContentsOfFile:path];

        NSMutableArray * arrM = [[NSMutableArray alloc]init];

        

        for (NSDictionary * dict in dicArr) {

            XCmessage * message = [XCmessage messageWithDict:dict];

            

            XCmessageFrame * messageFrame = [[XCmessageFrame allocinit];


            XCmessageFrame * lastFrame = [arrM lastObject];

            message.timeHid = [message.time                                 isEqualToString:lastFrame.message.time];

            messageFrame.message = message;

            [arrM addObject:messageFrame];      

        }

        _messageFrameModle = arrM;

    }   

    return _messageFrameModle;

}

@end

至此  界面搭建完毕


你可能感兴趣的:(使用UITableView纯代码模仿QQ聊天界面)