iOS基础-高级视图-UITableView--实例:QQ聊天

一、搭建UI界面
1.在普通视图控制器中放一个TableView
2.拖一个UIView,作为底部工具条
3.在UIView上放一个imageView,换成纯白色背景。然后在UIView上放一个按钮
4.将按钮的image属性换成语音图标,高度宽度均为44。x,y为0.(注意不要把背景图
片属性换成语音图标,会被拉伸。),同理再拖2个按钮换成相应图标
5.拖一个文本输入框,将其背景图片属性设置为相应的图片。

二、往TableView中放数据
1.设置数据源
2.新建一个模型MJMessage(plist里有3个属性,所以添加3条属性)

typedef enum {
MJMessageTypeMe = 0//自己发的
MJMessageTypeOther // 别人发的
} MJMessageType;
//聊天内容
@property (nonatomic,copy)NSString *text;
//消息发送时间
@property (nonatomic,copy)NSString *time;
//发送者
@property (nonatomic,copy)MJMessageType *type;
//是否显示时间
@property (nonatomic,assign)BOOL hideTime ;

//提供字典转模型的方法,并实现。
+(instancetype)messageWithDict:(NSDictionary *)dict;
-(instancetype)initWithDict:(NSDictionary *)dict;

+(instancetype)messageWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
}

-(instancetype)initWithDict:(NSDictionary *)dict
{
if(self = [super init]) {
       [self setValuesForKeysWithDictionary:dict];
   }
       return self;
}

 

3.加载模型数据

@property(nonatomic,strong)NSMutableArray *messageFrames;
-(NSMutableArray *)messageFrames
{
if(_messageFrames == nil){
//从plist里面加载出来字典数组
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"nessages.plist" ofType:nil]]; 
NSMutbleArray *msgArray = [NSMutableArray array];
// 将字典转成message模型
for (NSDictionary *dict in dictArray) {
//消息模型
MJMessage *msg = [MJMessage messageWithDict:dict];

//取出上一个模型
MJMessageFrame *lastMf = [mfArray lastObejct];
MJMessage *lastMsg = lastMf.message;

//判断两个消息的时间是否一致
msg.hideTime = [msg.time isEqualToString:lastMsg.time];

//frame模型
MJMessageFrame *mf = [MJMessageFrame alloc] init];
mf.message = msg;

//添加
[mfArray addObejct:mf];
}

      _messageFrames = mfArray;
   }
       return _messageFrames;
}

4.遵守协议并实现数据源方法

第一个数据源方法: 因为只有一列所以可以不实现。默认为1列
第二个数据源方法: numberOfRowsInSection:方法
return self.messageFrames.count;
第三个数据源方法: cellForRowAtIndexPath:方法
//1.创建cell
MJMessageCell *cell = [MJMessageCell cellWithTableView:tableView];
//2.给cell传递模型(在算完frame和cell高度之后进行这一步)
cell.messageFrame = self.messageFrames[indexPath.row];
//3.返回cell
return cell;

5.设置行高(设置代理遵守协议并实现代理方法 heightForRowAtIndexPath:方法)

{
    MJMessageFrame *mf = self.messageFrames[indexPath.row];
    return cellHeight;
}

6..通过代码自定义cell(cell的高度不一致)
1>新建一个Cell类MJMessageCell(注意要提供一个方法快速创建cell)
2>在cell的实现文件中重写initWithStyle:reuseIndetifier:方法
添加所有需要显示的子控件(不需要设置子控件的属性和frame,子控件要添加到contentView中)
进行子控件一次性的属性设置(有些属性只需要设置一次,比如字体、固定的图片)

//因为时间,头像和正文要在其他方法中使用,所以先将它们声明为成员变量
@property (nonatomic,weak)UILabel *timeView;
@property (nonatomic,weak)UIImageView *iconView;
@property (nonatomic,weak)UIButton *textView;
在这个例子中
//1.时间
UILabel *timeView = [[UILabel alloc] init];
timeView.textAlignment = NSTextAlignmentCenter
timeView.textColor = [UIColor lightGrayColor];
timeView.font = [UIFont systemFont:13];
[self.contentView addSubview:timeView];
self.timeView = timeView;
//2.头像
UIImageView *iconView = [UIImageView alloc] init];
[self.contentView addSubview:iconView];
self.iconView = iconView;
//3.正文
UIButton *textView = [[UIButton alloc] init];
textView.titleLabel.numberOfLines = 0//自动换行
textView.titleLabel.font = MJTextFont;
textView.contextEdgaInsets = UIEdgeInsetsMake(MJTextPadding,MJTextPadding,MJTextPadding,MJTextPadding); 
[textView setTitleColor:[UIColor blackColor] ForState:UIControlStateNormal];
[self.contentView addSubview:textView];
self.textView = textView;

//4.设置cell的背景色
self.backgroundColor = [UIColor clearColor];


3>提供2个模型

数据模型:存放文字数据/图片数据。已经创建好了,为MJMessage
frame模型:存放数据模型/所有子控件的frame/cell的高度
新建一个类MJMessageFrame并添加属性

//头像frame
@property(nonatomic,assgin,readonly)CGRect iconF;
//事件的frame
@property(nonatomic,assgin,readonly)CGRect timeF;
//正文的frame
@property(nonatomic,assgin,readonly)CGRect textF;
//cell的高度
@property(nonatomic,assgin,readonly)CGFloat cellHeight;
//数据模型
@class MJMessage;
@property(nonatomic,strong)MJMessage *message;

4>cell拥有一个frame模型(不要直接拥有数据模型)
@property(nonatomic,strong)MJMessageFrame *messageFrame;
5>重写frame模型属性的setter方法,在这个方法中设置子控件的显示数据和frame

-(void)setMessageFrame:(MJMessageFrame *)messageFrame
{
   _messageFrame = messageFrame;

   MJMessage *message = messageFrame.message; 
//1.时间
self.timeView.text = message.time;
self.timeView.frame = messageFrame.timeF;
//2.头像
NSString *icon = message.type == MJMessageTypeMe ? @"me"@"other";
self.iconView.image = [UIImage imageNamed:icon];
self.iconView.frame = messageFrame.iconF;
//3.正文
[self.textView setTitle:message.text forState:UIControlStateNormal];
self.textView.frame = messageFrame.textF;

//4.正文的背景
if(message.type == MJMessageTypeMe){ //自己发的(蓝色)
[self.textView setBackgroundImage:[UIImage resizableImage:@"chat_send_nor"] forState:UIControlStateNormal]; 
} else { //别人发的(白色)
     [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_recive_nor"] forState:UIControlStateNormal]; 
   }
} 

知识点:图片太小文字放不下怎么办-->设置按钮的内边距

-(void)setMessage:(MJMessage *)message
{
_message = message;
//间距
CGFloat padding = 10//屏幕的宽度
CGFloat screenW = [UIScreen mainScreen].bounds.width;

//1.时间
message.time
if (message.hideTime == NO ){ //显示时间
CGFloat timeX = 0;
CGFloat timeY = 0;
CGFloat timeW= screenW;
CGFloat timeH= 40;
_timeF = CGRectMake(timeX,timeY,timeW,timeH);
}
//2.头像
CGFloat iconY = CGRectGetMaxY(_timeF)+ padding;
CGFloat iconW = 40;
CGFloat iconH = 40;
CGFloat iconX;
if(message.type == MJMessageTypeOther) { //别人发的
iconX = padding;
} else { //自己发的
iconX = screenW - padding - iconW; 
} 
_iconF = CGRectMake(iconX,iconY,iconW,iconH);

//3.正文
CGFloat textY = iconY;


//文字计算的最大的尺寸
CGSize textMaxSize = CGSizeMake(150,MAXFLOAT);
//文字计算出来的真实尺寸(按钮内部label的尺寸)
CGSize textRealSize = [message.text sizeWithFont:MJTextFont  maxSize:textMaxSize];
//按钮最终的真实尺寸
CGSize textBtnSize = CGSizeMake(textRealSize.width + MJTextPadding * 2,textRealSize.height+ MJTextPadding * 2); 
CGFloat textX;
if(message.type == MJMesageTypeOther) {
textX= CGRectGetMaxX(_iconF)+padding;
} else { 
textX = iconX - padding - textBtnSize.width ;
}
}
_textF =(CGRect){{textX,textY}, textBtnSize};
}

//4.cell的高度
CGFloat textMaxY = CGRectGetMaxY(_textF);
CGFloat iconMaxY = CGRectGetMaxY(_iconF);
_cellHeight = MAX(textMaxY,iconMaxY) + padding;

6>frame模型数据的初始化已经采取惰性初始化的方式(每一个cell对应的frame模型数据只加载一次)

细节:
1.在viewDidLoad中去除分隔线
1>连线拿到tableView
2>self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
2.添加背景色

self.tableView.backgroundColor = [UIColor colorWithRed:224/255.0 green:224/255.0 Blue:224/255.0 alpha:1.0]; 

3.去掉状态栏

-(BOOL)prefersStatusBarHidden
{
    return YES;
}

4.cell不能被选中

self.tableView.allowsSelection = NO;

三、将实用的功能抽成分类(没必要使用类)
分类:给已经存在的类扩展一些方法
1.功能1: 计算文字尺寸
1>新建一个NSString的分类Extension
2>声明并实现下列方法

/* 
@param font 文字的字体
@param maxSize 文字的最大尺寸
*/
-(CGSzie)sizeWithFont:(UIFont *)font maxSize:(CGSzie)maxSize
{
    NSDictionary *attrs = @[NSFontAttributeName : font ];
    return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs  context:nil].size; 
}

3>导入分类头文件即可使用该分类里的方法。

2.功能二:返回一张可以随意拉伸不变形的图片
1>新建一个UIImage的分类Extension
2>声明并实现下列方法

+(UIImage *)resizableImage:(NSString *)name
{
UIImage *normal = [UIImage imageNamed:name];
CGFloat w = normal.size.width * 0.5;
CGFloat h = normal.size.height * 0.5return [normal resizableImageWithCapInsets:UIEdgeInsetsMake(h,w,h,w) ]; 
}

3>导入分类头文件即可使用该分类里的方法。

posted on 2015-08-24 14:10  Marshall_Yin  阅读(361)  评论(0编辑  收藏  举报