您的位置:首页 > 移动开发 > IOS开发

[iOS基础控件 - 6.9.1] 聊天界面Demo 代码

2014-12-08 21:54 411 查看
框架:




所有代码文件




Model:

//
//  Message.h
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//
// message信息模型,存储聊天记录

#import <Foundation/Foundation.h>

typedef enum {
MessageTypeMe = 0, // 我发出的信息
MessageTypeOhter = 1 // 对方发出的信息
} MessageType;

@interface Message : NSObject

/** 信息 */
@property(nonatomic, copy) NSString *text;

/** 发送时间 */
@property(nonatomic, copy) NSString *time;

/** 发送方 */
@property(nonatomic, assign) MessageType type;

/** 是否隐藏发送时间 */
@property(nonatomic, assign) BOOL hideTime;

- (instancetype) initWithDictionary:(NSDictionary *) dictionary;
+ (instancetype) messageWithDictionary:(NSDictionary *) dictionary;
+ (instancetype) message;

@end


//
//  Message.m
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import "Message.h"

@implementation Message

- (instancetype) initWithDictionary:(NSDictionary *) dictionary {
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dictionary];
}

return self;
}

+ (instancetype) messageWithDictionary:(NSDictionary *) dictionary {
return [[self alloc] initWithDictionary:dictionary];
}

+ (instancetype) message {
return [self messageWithDictionary:nil];
}

@end


//
//  MessageFrame.h
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//
// 存储每个cell内子控件的位置尺寸的frame

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "Message.h"

#define MESSAGE_TIME_FONT [UIFont systemFontOfSize:13]
#define MESSAGE_TEXT_FONT [UIFont systemFontOfSize:15]
#define TEXT_INSET 20

@interface MessageFrame : NSObject

/** 发送时间  */
@property(nonatomic, assign, readonly) CGRect timeFrame;

/** 头像 */
@property(nonatomic, assign, readonly) CGRect iconFrame;

/** 信息 */
@property(nonatomic, assign, readonly) CGRect textFrame;

/** 信息model */
@property(nonatomic, strong) Message *message;

/** cell的高度 */
@property(nonatomic, assign) CGFloat cellHeight;

@end


//
//  MessageFrame.m
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import "MessageFrame.h"
#import "NSString+Extension.h"

@implementation MessageFrame

/** 设置message,计算位置尺寸 */
- (void)setMessage:(Message *)message {
_message = message;

// 间隙
CGFloat padding = 10;

// 1.发送时间
if (NO == message.hideTime) {
CGFloat timeWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat timeHeight = 40;
CGFloat timeX = 0;
CGFloat timeY = 0;
_timeFrame = CGRectMake(timeX, timeY, timeWidth, timeHeight);
}

// 2.头像
CGFloat iconWidth = 40;
CGFloat iconHeight = 40;

// 2.1 根据信息的发送方调整头像位置
CGFloat iconX;
if (MessageTypeMe == message.type) {
// 我方,放在右边
iconX = [UIScreen mainScreen].bounds.size.width - padding - iconWidth;
} else {
// 对方,放在左边
iconX = padding;
}

CGFloat iconY = CGRectGetMaxY(_timeFrame) + padding;
_iconFrame = CGRectMake(iconX, iconY, iconWidth, iconHeight);

// 3.信息,尺寸可变
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
// 3.1 设置文本最大尺寸
CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * 10, MAXFLOAT);
// 3.2 计算文本真实尺寸
CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize];

// 3.3 按钮尺寸
CGSize btnSize = CGSizeMake(textRealSize.width + TEXT_INSET*2, textRealSize.height + TEXT_INSET*2);

// 3.4 调整信息的位置
CGFloat textX;
if (MessageTypeMe == message.type) {
// 我方,放在靠右
textX = CGRectGetMinX(_iconFrame) - btnSize.width - padding;
} else {
// 对方,放在靠左
textX = CGRectGetMaxX(_iconFrame) + padding;
}

CGFloat textY = iconY;
_textFrame = CGRectMake(textX, textY, btnSize.width, btnSize.height);

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

@end


View:

//
//  MessageCell.h
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import <UIKit/UIKit.h>

#define BACKGROUD_COLOR [UIColor colorWithRed:235/255.0 green:235/255.0 blue:235/255.0 alpha:1.0]

@class MessageFrame, Message;

@interface MessageCell : UITableViewCell

/** 持有存储了聊天记录和聊天框位置尺寸的frame */
@property(nonatomic, strong) MessageFrame *messageFrame;

/** 传入父控件tableView引用的构造方法 */
+ (instancetype) cellWithTableView:(UITableView *) tableView;

@end


//
//  MessageCell.m
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import "MessageCell.h"
#import "MessageFrame.h"
#import "UIImage+Extension.h"

@interface MessageCell()

// 定义cell内的子控件,用于保存控件,然后进行数据和位置尺寸的计算
/** 发送时间 */
@property(nonatomic, weak) UILabel *timeLabel;

/** 头像 */
@property(nonatomic, weak) UIImageView *iconView;

/** 信息 */
@property(nonatomic, weak) UIButton *textView;

@end

@implementation MessageCell

- (void)awakeFromNib {
// Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];

// Configure the view for the selected state
}

#pragma mark - 构造方法
// 自定义构造方法
+ (instancetype) cellWithTableView:(UITableView *) tableView {
static NSString *ID = @"message";

// 使用缓存池
MessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

// 创建一个新的cell
if (nil == cell) {
cell = [[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}

return cell;
}

// 重写构造方法,创建cell中的各个子控件
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

// 设置cell的背景色
self.backgroundColor = BACKGROUD_COLOR;

// 1.发送时间
UILabel *timeLabel = [[UILabel alloc] init];
[timeLabel setTextAlignment:NSTextAlignmentCenter];
[timeLabel setFont:MESSAGE_TIME_FONT];
[timeLabel setTextColor:[UIColor grayColor]];
[self.contentView addSubview:timeLabel];
self.timeLabel = timeLabel;

// 2.头像
UIImageView *iconView = [[UIImageView alloc] init];
[self.contentView addSubview:iconView];
self.iconView = iconView;

// 3.信息
UIButton *textView = [[UIButton alloc] init];
[textView setTitle:@"text" forState:UIControlStateNormal];
[textView.titleLabel setFont:MESSAGE_TEXT_FONT];

// 3.1 如果是浅色背景,记得设置字体颜色,因为按钮的字体颜色默认是白色
[textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[textView.titleLabel setNumberOfLines:0]; // 设置自动换行

// 3.2 调整文字的内边距
textView.contentEdgeInsets = UIEdgeInsetsMake(TEXT_INSET, TEXT_INSET, TEXT_INSET, TEXT_INSET);

[self.contentView addSubview:textView];
self.textView = textView;

return self;
}

#pragma mark - 加载数据
// 加载frame,初始化cell中子控件的数据、位置尺寸
- (void)setMessageFrame:(MessageFrame *) messageFrame {
_messageFrame = messageFrame;

// 1.发送时间
self.timeLabel.text = messageFrame.message.time;
self.timeLabel.frame = messageFrame.timeFrame;

// 2.头像
NSString *icon = (messageFrame.message.type == MessageTypeMe)? @"me":@"other";
self.iconView.image = [UIImage imageNamed:icon];
self.iconView.frame = messageFrame.iconFrame;

// 3.信息
[self.textView setTitle:messageFrame.message.text forState:UIControlStateNormal];
self.textView.frame = messageFrame.textFrame;

// 3.1 设置聊天框
NSString *chatImageNormalName;
NSString *chatImageHighlightedName;
if (MessageTypeMe == messageFrame.message.type) {
chatImageNormalName = @"chat_send_nor";
chatImageHighlightedName = @"chat_send_press_pic";
} else {
chatImageNormalName = @"chat_receive_nor";
chatImageHighlightedName = @"chat_receive_press_pic";
}

UIImage *chatImageNormal = [UIImage resizableImage:chatImageNormalName];
UIImage *chatImageHighlighted = [UIImage resizableImage:chatImageHighlightedName];
[self.textView setBackgroundImage:chatImageNormal forState:UIControlStateNormal];
[self.textView setBackgroundImage:chatImageHighlighted forState:UIControlStateHighlighted];
}

@end


Controller:

//
//  ViewController.m
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import "ViewController.h"
#import "Message.h"
#import "MessageCell.h"
#import "MessageFrame.h"

@interface ViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>

/** 聊天区tableView */
@property (weak, nonatomic) IBOutlet UITableView *tableView;

/** 信息记录数据 */
@property(nonatomic, strong) NSMutableArray *messages;

/** 信息输入框 */
@property (weak, nonatomic) IBOutlet UITextField *inputView;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

// 设置dataSource
self.tableView.dataSource = self;

// 设置tableView的delegate
self.tableView.delegate = self;

// 设置tableView背景色,当键盘呼出隐藏的时候,避免默认的黑色背景出现太突兀
self.tableView.backgroundColor = BACKGROUD_COLOR;

// 设置聊天区TableView
// 不使用分割线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// 禁止选中cell
[self.tableView setAllowsSelection:NO];

// 设置虚拟键盘监听器
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];

// 设置TextField文字左间距
self.inputView.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 8, 0)];
self.inputView.leftViewMode = UITextFieldViewModeAlways;

// 设置信息输入框的代理
self.inputView.delegate = self;
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (BOOL)prefersStatusBarHidden {
return YES;
}

#pragma mark - 数据加载
/** 延迟加载plist文件数据 */
- (NSMutableArray *)messages {
if (nil == _messages) {
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]];

NSMutableArray *mdictArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
Message *message = [Message messageWithDictionary:dict];

// 判断是否发送时间与上一条信息的发送时间相同,若是则不用显示了
MessageFrame *lastMessageFrame = [mdictArray lastObject];
if (lastMessageFrame && [message.time isEqualToString:lastMessageFrame.message.time]) {
message.hideTime = YES;
}

MessageFrame *messageFrame = [[MessageFrame alloc] init];
messageFrame.message = message;
[mdictArray addObject:messageFrame];
}

_messages = mdictArray;
}

return _messages;
}

#pragma mark - dataSource方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return  self.messages.count;
}

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MessageCell *cell = [MessageCell cellWithTableView:self.tableView];
cell.messageFrame = self.messages[indexPath.row];

return cell;
}

#pragma mark - tableView代理方法
/** 动态设置每个cell的高度 */
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
MessageFrame *messageFrame = self.messages[indexPath.row];
return messageFrame.cellHeight;
}

#pragma mark - scrollView 代理方法
/** 点击拖曳聊天区的时候,缩回键盘 */
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 1.缩回键盘
[self.view endEditing:YES];
}

#pragma mark - 监听事件
- (void) keyboardWillChangeFrame:(NSNotification *) note {
// 1.取得弹出后的键盘frame
CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

// 2.键盘弹出的耗时时间
CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];

// 3.键盘变化时,view的位移,包括了上移/恢复下移
CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height;

[UIView animateWithDuration:duration animations:^{
self.view.transform = CGAffineTransformMakeTranslation(0, transformY);
}];
}

#pragma mark - TextField 代理方法
/** 回车响应事件 */
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// 我方发出信息
[self sendMessageWithContent:textField.text andType:MessageTypeMe];

// 自动回复
[self sendMessageWithContent:[NSString stringWithFormat:@"%@\n%@", textField.text, @"你妹!!!"] andType:MessageTypeOhter];

// 消除消息框内容
self.inputView.text = nil;

[self.tableView reloadData];

// 滚动到最新的消息
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - 1 inSection:0];
[self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

return YES; // 返回值意义不明
}

// 发送消息
- (void) sendMessageWithContent:(NSString *) text andType:(MessageType) type {
// 获取当前时间
NSDate *date = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MMM-dd hh:mm:ss";
NSString *dateStr = [formatter stringFromDate:date];

// 我方发出信息
NSDictionary *dict = @{@"text":text,
@"time":dateStr,
@"type":[NSString stringWithFormat:@"%d", type]};

Message *message = [[Message alloc] init];
[message setValuesForKeysWithDictionary:dict];
MessageFrame *messageFrame = [[MessageFrame alloc] init];
messageFrame.message = message;

[self.messages addObject:messageFrame];
}

@end


工具类:

//
//  NSString+Extension.h
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//
// NSString扩展类

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface NSString (Extension)

/** 测量文本的尺寸 */
- (CGSize) sizeWithFont:(UIFont *)font maxSize:(CGSize) maxSize;

@end


//
//  NSString+Extension.m
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import "NSString+Extension.h"

@implementation NSString (Extension)

/** 测量文本的尺寸 */
- (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize {
NSDictionary *attrs = @{NSFontAttributeName: font};
CGSize size =  [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;

return size;
}

@end


//
//  UIImage+Extension.h
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//
// NSImage 类的扩展

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface UIImage (Extension)

+ (UIImage *) resizableImage:(NSString *) imageName;

@end


//
//  UIImage+Extension.m
//  QQChatDemo
//
//  Created by hellovoidworld on 14/12/8.
//  Copyright (c) 2014年 hellovoidworld. All rights reserved.
//

#import "UIImage+Extension.h"

@implementation UIImage (Extension)

+ (UIImage *) resizableImage:(NSString *) imageName {
UIImage *image = [UIImage imageNamed:imageName];
// 取图片中部的1 x 1进行拉伸
UIEdgeInsets insets = UIEdgeInsetsMake(image.size.height/2, image.size.width/2, image.size.height/2 + 1, image.size.width/2 + 1);
return [image resizableImageWithCapInsets:insets];
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: