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>

@classSLQMessage;

@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"

@interfaceSLQMessageCell ()

@property (weak, nonatomic) IBOutletUILabel *timeLable;

@property (weak, nonatomic) IBOutletUIButton *meBtn;

@property (weak, nonatomic) IBOutletUIImageView *meImage;

@property (weak, nonatomic) IBOutletUIButton *otherBtn;

@property (weak, nonatomic) IBOutletUIImageView *otherImage;

@end

@implementation SLQMessageCell

 

// 重写setter方法

- (void)setMessage:(SLQMessage *)message

{

_message = message;

self.backgroundColor = [UIColorbrownColor];

if(message.isHideTime) // 隐藏时间

{

self.timeLable.hidden = YES;

[self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

make.height.equalTo(0); // 高度为0

}];

}

else

{

self.timeLable.text = message.time;

self.timeLable.hidden = NO;

[self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

make.height.equalTo(22);

}];

}

if (message.type == SLQMessageTypeMe)

{

[selfsetShowBtn:self.meBtnWithShowImage:self.meImageWithHideBtn:self.otherBtnWithHideImage:self.otherImage];

}

if (message.type == SLQMessageTypeOther)

{

[selfsetShowBtn:self.otherBtnWithShowImage:self.otherImageWithHideBtn:self.meBtnWithHideImage:self.meImage];

}

}

        因为每次显示cell都要进行计算,将cell的显示封装到方法中。

// 显示隐藏控件并计算控件的高度

- (void)setShowBtn:(UIButton *)showBtn WithShowImage:(UIImageView *)showImage WithHideBtn:(UIButton *)hideBtn WithHideImage:(UIImageView *)hideImage

{

[showBtn setTitle:self.message.textforState:UIControlStateNormal];

// 隐藏其他

hideBtn.hidden = YES;

hideImage.hidden = YES;

// 显示自己

showBtn.hidden = NO;

showImage.hidden = NO;

// 强制更新

[selflayoutIfNeeded];

// 更新约束,设置按钮的高度就是textLable的高度

[showBtn updateConstraints:^(MASConstraintMaker *make) {

CGFloat buttonH = showBtn.titleLabel.frame.size.height;//

make.height.equalTo(buttonH);

}];

// 强制更新

[selflayoutIfNeeded];

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中后选中图片设置。

 可以通过代码设置按钮的内间距

// 可以这样设置内间距

, , , );

[showBtn setTitleEdgeInsets:edge];

 或者直接在按钮的属性里设置

  设置过间距后,就可以计算btn的高度时,因为textlable的高度不固定,所以让btn的高度等于textLable 的高度。但是又因为按钮背景图片的边缘有一部分是透明的,如下:红色是按钮,蓝色是图片。  

   

 所以显示文字高度会,这里对其按钮高度 + 30,而textLable默认会水平垂直居中。

5、在控制器中得实现方法和以往的一样

只需要在这里判断以下消息显示的时间是否一致,如果一致就隐藏。

- (NSMutableArray *)messages

{

if (_messages == nil)

{

NSArray *dictArray = [NSArrayarrayWithContentsOfFile:[[NSBundlemainBundle] pathForResource:@"messages.plist"ofType:nil]];

NSMutableArray *tempArray = [NSMutableArrayarray];

// 记录上一个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 {

[superviewDidLoad];

}

/**

*  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 = [SLQMessageCellcellWithTableView: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 = [UIColorbrownColor];

if(message.isHideTime) // 隐藏时间

{

self.timeLable.hidden = YES;

[self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

make.height.equalTo(0);

}];

}

else

{

self.timeLable.text = message.time; // 显示时间

self.timeLable.hidden = NO;

[self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

make.height.equalTo(22);

}];

}

//

[self.contentBtnsetTitle:message.textforState:UIControlStateNormal];

// 强制布局

[selflayoutIfNeeded];

// 添加约束

[self.contentBtnupdateConstraints:^(MASConstraintMaker *make) {

CGFloat textLableHeight = self.contentBtn.titleLabel.frame.size.height + 30;

make.height.equalTo(textLableHeight);

}];

[selflayoutIfNeeded];

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;

}

 
好了,效果一样。
 
 

更新: 滚动到最新的一行

响应一个发送按钮,点击发送按钮后获取文本框数据,并显示到tableView中。
 
发送按钮如下
 

/**

*  发送信息

*/

- (IBAction)sendMessage:(id)sender

{

// 获取文字内容

NSString *message = self.textField.text;

SLQMessageType type = (arc4random_uniform(2));

// 更新模型数据

SLQMessage *mess = [[SLQMessage alloc] init];

mess.time = @"2015-11-23";

mess.text = message;

mess.type = type;

// 设置模型数据,添加到数组

[self.messages addObject:mess];

// 刷新表格

[self.tableViewreloadData];

// 滚动到底部方法

[selfscrollToBottom];

}

 
滚动底部方法
 

// 滚动到底部

- (void)scrollToBottom

{

CGFloat yOffset = 0;

// 如果tableView的高度大于tableView的自有有高度,y轴偏移量就等于contentSize - bounds

if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {

yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;

}

// 设置偏移量为最底部

[self.tableViewsetContentOffset:CGPointMake(0, yOffset) animated:NO];

}

IOS开发学习笔记043-QQ聊天界面实现的更多相关文章

  1. Objective-c——UI基础开发第八天(QQ聊天界面)

    一.知识点: QQ聊天界面 双模型的使用(dataModel和frameModel) UITextField的使用 通知的使用 拉伸图片的两种方法(slicing/image对象的resizeable ...

  2. iOS开发学习笔记:基础篇

    iOS开发需要一台Mac电脑.Xcode以及iOS SDK.因为苹果设备都具有自己封闭的环境,所以iOS程序的开发必须在Mac设备上完成(当然,黑苹果应该也是可以的,但就需要花很多的精力去折腾基础环境 ...

  3. ios开发学习笔记(1)

    objective-c基础总结 第一二章 1.application:didiFinishLauchingWithOptions:程序启动后立即执行 2.启动界面代码格式:self.window = ...

  4. iOS开发——UI_swift篇&TableView自定义聊天界面

    TableView自定义聊天界面   1,下面是一个放微信聊天界面的消息展示列表,实现的功能有: (1)消息可以是文本消息也可以是图片消息 (2)消息背景为气泡状图片,同时消息气泡可根据内容自适应大小 ...

  5. IOS开发学习笔记031-代码实现微博界面

    微博界面如下 1.准备资源文件 新建一个plist文件,添加条目,root类型是array,子类型是Dictionary 2.更改父类,实现代理方法 接下来得实现过程如上一篇文章,改变父类为UITab ...

  6. IOS开发学习笔记030-xib实现淘宝界面

    使用xib文件实现界面,然后通过模型更新数据. 1.使得控制器继承自UITableViewController 2.创建xib文件,实现界面如下:一个UIImageView,两个lable 3.新建一 ...

  7. IOS开发学习笔记038-autolayout 自动布局 界面实现

    在storyboard/xib文件中实现自动布局 autolayout 1.注意事项 autolayout和frame属性是有冲突的,所以如果准备使用autolayout,就不要再代码中对控件的fra ...

  8. ios开发学习笔记(这里一定有你想要的东西,全部免费)

    1,Search Bar 怎样去掉背景的颜色(storyboard里只能设置background颜色,可是发现clear Color无法使用). 其实在代码里还是可以设置的,那就是删除背景view [ ...

  9. iOS开发学习笔记

    1 常用的第三方工具 1.1 iPhone Simulator 测试程序需要模拟器iPhone Simulator 1.2 设计界面需要Interface Builder,Interface Buil ...

随机推荐

  1. javascript字符串格式化string.format

    String.prototype.format = function () { var values = arguments; return this.replace(/\{(\d+)\}/g, fu ...

  2. 用rem实现h5页面的编写

    一 静态页面的布局 将这段代码加到script中 (function(doc, win) { var docEl = doc.documentElement, resizeEvt = 'orienta ...

  3. 用TextKit实现表情混排

      Textkit是iOS7新推出的类库,其实是在之前推出的CoreText上的封装,有了这个TextKit,以后不用再拿着CoreText来做累活了,根据苹果的说法,他们开发了两年多才完成,而且他们 ...

  4. 302和VS启动后网站拒绝访问的解决方案

    网页状态302代表的是重定向的意思,就是网页跳转的一种状态 网站拒绝访问的时候可以在输出窗口查看是否有内容输出,如果没有说明启动网站的端口可能被占用,在网站项目——属性——web——项目中把地址的端口 ...

  5. POJ 3126 Prime Path(筛法,双向搜索)

    题意:一个4位的素数每次变动一个数位,中间过程也要上素数,问变成另一个的最小步数. 线性筛一遍以后bfs就好.我写的双向,其实没有必要. #include<cstdio> #include ...

  6. 【洛谷3759】[TJOI2017] 不勤劳的图书管理员(树套树)

    点此看题面 大致题意: 给定一个序列,每个元素有两个属性\(a_i\)和\(v_i\),每次操作改变两个元素的位置,求每次操作后\(\sum{v_i+v_j}[i<j,a_i>a_j]\) ...

  7. 2018.2.2 JavaScript中的封装

    JavaScript中的封装 1.封装的概念 通过将一个方法或者属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束,这样可以使代码 ...

  8. Java环境变量搭建(Linux环境)

    1. 下载解压JDK压缩包 例如:解压到 /opt/jdk1.7.0_80 下 2. 添加环境变量到 /etc/profile 文件中 vi /etc/profile 在文件末尾追加如下内容: exp ...

  9. axiospost请求向后端提交数据

    Axios向后端提交数据容易接收不到原因是传参方式是request payload,参数格式是json,而并非用的是form传参,所以在后台用接收form数据的方式接收参数就接收不到了.post表单请 ...

  10. 重载&重写

    重载:同一个类中,方法名相同,方法参数不同(参数个数.参数类型),返回类型无关,所以返回类型不能作为重载的区别依据. 重写:子父类中,子类的方法名.参数位置.参数个数.返回类型和父类一致,方法体不同 ...