概述
QQ聊天界面实现
效果如下:
实现过程:
1、首先实现基本界面
头像使用 UIImageView :
文字消息使用 UIButton
标签使用 UILable :水平居中
所有元素在一个cell中,在加载cell时进行判断显示和隐藏。
合理设置各个控件之间的约束关系。主要是UIIimageVIew和UIButton顶部对齐,间距为10。UIButton的宽度设置一个约束范围,比如说 (>=60 && <=300);
底部添加一个UIView ,添加输入框等。
2、创建模型文件
所有元素在一个cell中,在加载cell时进行判断显示和隐藏。
按照message.plist文件内容添加需要的属性,然后添加一个cellHeight属性计算cell高度,和一个决定是否显示时间到cell得属性hideTime。
#import <UIKit/UIKit.h>
// 枚举类型,
typedefenum {
SLQMessageTypeMe = 0 ,
SLQMessageTypeOther = 1
}SLQMessageType;
@interface SLQMessage : NSObject
/* 内容 */
@property ( strong , nonatomic ) NSString *text;
/* 时间 */
@property ( strong , nonatomic ) NSString *time;
/* 类型 */
@property ( assign , nonatomic ) SLQMessageType type;
/*cellHeight*/
@property ( assign , nonatomic ) CGFloat cellHeight;
/* 是否隐藏时间 */
@property ( assign , nonatomic , getter =isHideTime) BOOL hideTime;
+ ( instancetype )MessageWithDict:( NSDictionary *)dict;
@end
实现文件
#import "SLQMessage.h"
@implementation SLQMessage
+( instancetype )MessageWithDict:( NSDictionary *)dict
{
SLQMessage *message = [[ SLQMessage alloc ] init ];
[message setValuesForKeysWithDictionary :dict];
return message;
}
@end
这里需要注意的就是枚举类型的使用,如果在一个类中要定义枚举类型,那么命名规则就是:
以类名开头后面直接跟操作标识;如 SLQMessage + Type ;
3、实现对cell操作的封装
#import <UIKit/UIKit.h>
@class SLQMessage ;
@interface SLQMessageCell : UITableViewCell
/* 模型对象 */
@property ( strong , nonatomic ) SLQMessage *message;
+ ( instancetype )cellWithTableView:( UITableView *)tableView;
@end
对tableView的每一个控件拖线建立关联。然后重写setter方法,对控件进行赋值。
#import "SLQMessageCell.h"
#import "SLQMessage.h"
//define this constant if you want to use Masonry without the 'mas_' prefix
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax
#define MAS_SHORTHAND_GLOBALS
#import "Masonry.h"
@interface SLQMessageCell ()
@property ( weak , nonatomic ) IBOutlet UILabel *timeLable;
@property ( weak , nonatomic ) IBOutlet UIButton *meBtn;
@property ( weak , nonatomic ) IBOutlet UIImageView *meImage;
@property ( weak , nonatomic ) IBOutlet UIButton *otherBtn;
@property ( weak , nonatomic ) IBOutlet UIImageView *otherImage;
@end
@implementation SLQMessageCell
// 重写 setter 方法
- ( void )setMessage:( SLQMessage *)message
{
_message = message;
self . backgroundColor = [ UIColor brownColor ];
if (message. isHideTime ) // 隐藏时间
{
self . timeLable . hidden = YES ;
[ self . timeLable updateConstraints :^( MASConstraintMaker *make) {
make. height . equalTo ( 0 ); // 高度为0
}];
}
else
{
self . timeLable . text = message. time ;
self . timeLable . hidden = NO ;
[ self . timeLable updateConstraints :^( MASConstraintMaker *make) {
make. height . equalTo ( 22 );
}];
}
if (message. type == SLQMessageTypeMe )
{
[ self setShowBtn : self . meBtn WithShowImage : self . meImage WithHideBtn :self . otherBtn WithHideImage : self . otherImage ];
}
if (message. type == SLQMessageTypeOther )
{
[ self setShowBtn : self . otherBtn WithShowImage : self . otherImage WithHideBtn : self . meBtn WithHideImage : self . meImage ];
}
}
因为每次显示cell都要进行计算,将cell的显示封装到方法中。
// 显示隐藏控件并计算控件的高度
- ( void )setShowBtn:( UIButton *)showBtn WithShowImage:( UIImageView*)showImage WithHideBtn:( UIButton *)hideBtn WithHideImage:( UIImageView*)hideImage
{
[showBtn setTitle : self . message . text forState : UIControlStateNormal ];
// 隐藏其他
hideBtn. hidden = YES ;
hideImage. hidden = YES ;
// 显示自己
showBtn. hidden = NO ;
showImage. hidden = NO ;
// 强制更新
[ self layoutIfNeeded ];
// 更新约束,设置按钮的高度就是 textLable 的高度
[showBtn updateConstraints :^( MASConstraintMaker *make) {
CGFloat buttonH = showBtn. titleLabel . frame . size . height ; //
make. height . equalTo (buttonH);
}];
// 强制更新
[ self layoutIfNeeded ];
CGFloat btnMaxY = CGRectGetMaxY (showBtn. frame );
CGFloat imageMaxY = CGRectGetMaxY (showImage. frame );
// 设置 cell 高度
self . message . cellHeight = MAX (btnMaxY, imageMaxY) + 10 ;
}
其他方法和以往一样
+ ( instancetype )cellWithTableView:( UITableView *)tableView
{
SLQMessageCell *cell = [tableView dequeueReusableCellWithIdentifier :@"message" ];
return cell;
}
- ( void )awakeFromNib {
// Initialization code
// 多行显示
self . meBtn . titleLabel . numberOfLines = 0 ;
self . otherBtn . titleLabel . numberOfLines = 0 ;
}
4、接下来说说按钮背景的问题
按钮背景默认填充整个按钮,但是默认情况下的填充效果不是很好。
如下代码:
UIImageView *imageView = [[ UIImageView alloc ] init ];
imageView. frame = CGRectMake ( 10 , 10 , 300 , 200 );
UIImage *image = [ UIImage imageNamed : @"chat_send_nor" ];
// 方法 1 , 设置拉伸间距,默认拉伸中心 1*1 像素
//image = [image stretchableImageWithLeftCapWidth:image.size.width * 0.5 topCapHeight:image.size.height * 0.5];
// 方法 2 设置边界
UIEdgeInsets edge = UIEdgeInsetsMake ( 50 , 40 , 40 , 40 );
//image = [image resizableImageWithCapInsets:edge ];
// UIImageResizingModeStretch 拉伸模式
// UIImageResizingModeTile 填充模式
image = [image resizableImageWithCapInsets :edge resizingMode :UIImageResizingModeStretch ];
// 方法 3
// 在 images.xcassets 中对图片进行设置
imageView. image = image;
[ self . view addSubview :imageView];
// 对比图片
UIImageView *imageView1 = [[ UIImageView alloc ] init ];
imageView1. frame = CGRectMake ( 10 , 210 , 300 , 200 );
UIImage *image1 = [ UIImage imageNamed : @"chat_send_nor" ];
imageView1. image = image1;
[ self . view addSubview :imageView1];
会出现以下效果,默认是下边的图片,所以有必要对图片进行拉伸。
其中方法3的设置是将图片导入Image.xcassets中后选中图片设置。
可以通过代码设置按钮的内间距
// 可以这样设置内间距
UIEdgeInsets edge = UIEdgeInsetsMake ( 15 , 15 , 15 , 15 );
[showBtn setTitleEdgeInsets :edge];
或者直接在按钮的属性里设置
设置过间距后,就可以计算btn的高度时,因为textlable的高度不固定,所以让btn的高度等于textLable 的高度。但是又因为按钮背景图片的边缘有一部分是透明的,如下:红色是按钮,蓝色是图片。
所以显示文字高度会,这里对其按钮高度 + 30,而textLable默认会水平垂直居中。
5、在控制器中得实现方法和以往的一样
只需要在这里判断以下消息显示的时间是否一致,如果一致就隐藏。
- ( NSMutableArray *)messages
{
if ( _messages == nil )
{
NSArray *dictArray = [ NSArray arrayWithContentsOfFile :[[ NSBundle mainBundle ] pathForResource : @"messages.plist" ofType : nil ]];
NSMutableArray *tempArray = [ NSMutableArray array ];
// 记录上一个 message ,判断是否显示时间
SLQMessage *lastMessage = nil ;
for ( NSDictionary *dict in dictArray)
{
SLQMessage *message = [ SLQMessage MessageWithDict :dict];
message. hideTime = [message. time isEqualToString :lastMessage. time ];
[tempArray addObject :message];
// 重新赋值
lastMessage = message;
}
_messages = tempArray;
}
return _messages ;
}
- ( void )viewDidLoad {
[ super viewDidLoad ];
}
/**
* tableView 行数
*/
- ( NSInteger )tableView:( UITableView *)tableView numberOfRowsInSection:( NSInteger )section
{
//NSLog(@"%zd",self.messages.count);
returnself . messages . count ;
}
/**
* 设置每一个 cell
*/
- ( UITableViewCell *)tableView:( UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
SLQMessageCell *cell = [ SLQMessageCell cellWithTableView :tableView];
cell. message = self . messages [indexPath. row ];
return cell;
}
/**
* 设置 cell 高度
*/
- ( CGFloat )tableView:( UITableView *)tableView heightForRowAtIndexPath:( NSIndexPath *)indexPath
{
SLQMessage *message = self . messages [indexPath. row ];
return message. cellHeight ;
}
/**
* 给出预估高度
*/
- ( CGFloat )tableView:( UITableView *)tableView estimatedHeightForRowAtIndexPath:( NSIndexPath *)indexPath
{
return 200 ;
}
@end
总结:
这是一种方法,还有其他的实现方法,接下来尝试一下。
5、用两个cell实现界面
只需改动一些代码就行。
1、改动每个cell的标志 一个是me,一个是other
2、修改setter方法
// 重写 setter 方法
- ( void )setMessage:( SLQMessage *)message
{
_message = message;
self . backgroundColor = [ UIColor brownColor ];
if (message. isHideTime ) // 隐藏时间
{
self . timeLable . hidden = YES ;
[ self . timeLable updateConstraints :^( MASConstraintMaker *make) {
make. height . equalTo ( 0 );
}];
}
else
{
self . timeLable . text = message. time ; // 显示时间
self . timeLable . hidden = NO ;
[ self . timeLable updateConstraints :^( MASConstraintMaker *make) {
make. height . equalTo ( 22 );
}];
}
//
[ self . contentBtn setTitle :message. text forState : UIControlStateNormal ];
// 强制布局
[ self layoutIfNeeded ];
// 添加约束
[ self . contentBtn updateConstraints :^( MASConstraintMaker *make) {
CGFloat textLableHeight = self . contentBtn . titleLabel . frame . size . height + 30 ;
make. height . equalTo (textLableHeight);
}];
[ self layoutIfNeeded ];
CGFloat btnMaxY = CGRectGetMaxY ( self . contentBtn . frame );
CGFloat iconMaxY = CGRectGetMaxY ( self . iconImage . frame );
message. cellHeight = MAX (btnMaxY, iconMaxY);
}
3、修改返回cell对象的方法,传入一个message用来判断是哪个cell
/**
* 返回 cell 对象
*/
+ ( instancetype )cellWithTableView:( UITableView *)tableView andMessage:( SLQMessage *)message
{
NSString *ID = (message. type == SLQMessageTypeMe )? @"me" : @"other" ;
SLQMessageCell *cell = [tableView dequeueReusableCellWithIdentifier :ID];
return cell;
}
4、在控制器中设置如下
/**
* 设置每一个 cell
*/
- ( UITableViewCell *)tableView:( UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
// 获取一个 cell ,根据类型
SLQMessageCell *cell = [ SLQMessageCell cellWithTableView :tableView andMessage : self . messages [indexPath. row ]];
cell. message = self . messages [indexPath. row ];
return cell;
}
好了,效果一样。
最后
以上就是危机保温杯为你收集整理的QQ聊天界面实现的全部内容,希望文章能够帮你解决QQ聊天界面实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复