#import <UIKit/UIKit.h>

 @interface AppDelegate : UIResponder <UIApplicationDelegate>

 @property (strong, nonatomic) UIWindow *window;

 @end
 #import "AppDelegate.h"
#import "YXYCViewController.h"
@interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = [[YXYCViewController alloc] init]; [self.window makeKeyAndVisible];
return YES;
} @end
 #import <UIKit/UIKit.h>

 @interface YXYCViewController : UIViewController

 @end
 #import "YXYCViewController.h"
#import "YXYCChatUser.h"
#import "YXYCChatTableViewDataSource.h"
#import <QuartzCore/QuartzCore.h>
#import "YXYCChatTableView.h"
#import "YXYCChatData.h"
#define lineHeight 16.0f
#define scale [UIScreen mainScreen].bounds.size.width/320.0
@interface YXYCViewController ()<YXYCChatTableViewDataSource,UITextViewDelegate>
{
UIView *textInputView;
UITextField *textField;
NSMutableArray *Chats; UIView *sendView;
UIButton *sendButton;
UITextView *msgText;
BOOL composing;
float prevLines;
YXYCChatUser *me;
YXYCChatUser *you;
//键盘的高度
float keyboardHeight;
}
@property (strong, nonatomic) YXYCChatTableView *chatTable;
@end @implementation YXYCViewController CGRect appFrame;
- (void)viewDidLoad {
[super viewDidLoad];
// 添加监听
[self addNotification];
// 设置背景颜色
self.view.backgroundColor = [UIColor lightGrayColor];
// 初始化tableView
[self setupTableView];
// 初始化view和textView
[self setupUIViewAndUITextView];
// 初始化sendButton
[self setupSendButton];
// 加载数据
[self loadData]; }
// 初始化YXYCChatTableView
- (void)setupTableView
{
appFrame = [[UIScreen mainScreen] bounds];
// 创建一个self.chatTable(YXYCChatTableView类型)
self.chatTable = [[YXYCChatTableView alloc] initWithFrame:CGRectMake(, , [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height - ) style:UITableViewStylePlain];
// 设置self.chatTable的背景颜色
self.chatTable.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.chatTable];
}
// 初始化sendView和msgText
- (void)setupUIViewAndUITextView
{
// 初始化sendView
sendView = [[UIView alloc] initWithFrame:CGRectMake(, appFrame.size.height-, appFrame.size.width, )];
sendView.backgroundColor = [UIColor blueColor];
sendView.alpha = 0.9;
// 初始化msgText
msgText = [[UITextView alloc] initWithFrame:CGRectMake(, , *scale, )];
msgText.backgroundColor = [UIColor whiteColor];
msgText.textColor = [UIColor blackColor];
msgText.font = [UIFont boldSystemFontOfSize:];
// 自动调整与父视图的位置 UIViewAutoresizingFlexibleHeight 自动调整自己的高度,保证与superView顶部和底部的距离不变;UIViewAutoresizingFlexibleTopMargin 自动调整与superView顶部的距离,保证与superView底部的距离不变。
msgText.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
// 设置圆角半径
msgText.layer.cornerRadius = 10.0f;
// 设置键盘返回键的类型
msgText.returnKeyType = UIReturnKeySend;
msgText.showsHorizontalScrollIndicator = NO;
msgText.showsVerticalScrollIndicator = NO;
// 设置UITextView代理
msgText.delegate = self;
[sendView addSubview:msgText];
msgText.contentInset = UIEdgeInsetsMake(, , , );
[self.view addSubview:sendView];
}
// 初始化sendButton
- (void)setupSendButton
{
sendButton = [[UIButton alloc] initWithFrame:CGRectMake(*scale, , , )];
sendButton.backgroundColor = [UIColor lightGrayColor];
[sendButton addTarget:self action:@selector(sendMessage) forControlEvents:UIControlEventTouchUpInside];
// 自动调整与父视图的位置
sendButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
sendButton.layer.cornerRadius = 6.0f;
[sendButton setTitle:@"Send" forState:UIControlStateNormal];
[sendView addSubview:sendButton];
}
// 加载数据
- (void)loadData
{
// 创建两个YXYCChatUser对象
me = [[YXYCChatUser alloc] initWithUsername:@"Peter" avatarImage:[UIImage imageNamed:@"me.png"]];
you = [[YXYCChatUser alloc] initWithUsername:@"You" avatarImage:[UIImage imageNamed:@"noavatar.png"]];
//创建几个YXYCChatData对象
YXYCChatData *first = [YXYCChatData dataWithText:@"Hey,how are you doing? I'm in Paris take a look at this picture." date:[NSDate dateWithTimeIntervalSinceNow:-] type:ChatTypeMine andUser:me];
YXYCChatData *second = [YXYCChatData dataWithImage:[UIImage imageNamed:@"eiffeltower.jpg"] date:[NSDate dateWithTimeIntervalSinceNow:-] type:ChatTypeMine andUser:me];
YXYCChatData *third = [YXYCChatData dataWithText:@"Wow..Really cool picture out there. Wish I could be with you" date:[NSDate dateWithTimeIntervalSinceNow:-] type:ChatTypeSomeone andUser:you];
YXYCChatData *forth = [YXYCChatData dataWithText:@"Maybe next time you can come with me." date:[NSDate dateWithTimeIntervalSinceNow:+] type:ChatTypeMine andUser:me];
// 通过YXYCChatData对象来初始化Chats(数组)
Chats = [[NSMutableArray alloc] initWithObjects:first,second,third,forth, nil];
// 确认YXYCChatTableViewDataSource的代理
self.chatTable.chatDataSource = self;
// 调用reloadData方法(此方法已被重写)
[self.chatTable reloadData];
}
// 页面消失时移除监听
- (void)viewDidDisappear:(BOOL)animated
{
[self removeNotification];
} - (void)sendMessage
{
composing = NO;
YXYCChatData *thisChat = [YXYCChatData dataWithText:msgText.text date:[NSDate date] type:ChatTypeMine andUser:me];
[Chats addObject:thisChat];
[self.chatTable reloadData];
[self showTableView];
// 注销msgText为第一响应者
[msgText resignFirstResponder];
// 清除原来的文本内容
msgText.text = @"";
sendView.frame = CGRectMake(, appFrame.size.height - , *scale, );
// TODO:注意
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[Chats count] inSection:];
// tableView 滚到底部
[self.chatTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
} #pragma mark -UITextViewDelegat-
//如果用户按回车键,则认为结束编辑,并进行发送消息
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ([text isEqualToString:@"\n"]) {
[self sendMessage];
return NO;
}
return YES;
} /**
* @return 返回输入文本在UITextView上的高度
*/
- (CGFloat)textY
{
// 设置字体的样式和大小
UIFont *systemFont = [UIFont boldSystemFontOfSize:];
int width = 225.0, heigth = 10000.0;
NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
[atts setObject:systemFont forKey:NSFontAttributeName];
// 根据文字量返回CGRect
CGRect size = [msgText.text boundingRectWithSize:CGSizeMake(width, heigth) options:NSStringDrawingUsesLineFragmentOrigin attributes:atts context:nil];
//获取文字的高度
float textHeight = size.size.height; float lines = textHeight / lineHeight;
if (lines >= ) {
lines = ;
}
if ([msgText.text length] == ) {
lines = 0.9375f;
}
return - (lines * lineHeight) + lineHeight;
}
// 输入文字时,重新计算和布置sendView的frame,并保持在键盘上方
- (void)textViewDidChange:(UITextView *)textView
{
// 设置文字的样式和大小
UIFont *systemFont = [UIFont boldSystemFontOfSize:];
int width = 225.0, height = 10000.0;
NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
[atts setObject:systemFont forKey:NSFontAttributeName]; CGRect size = [msgText.text boundingRectWithSize:CGSizeMake(width, height) options:NSStringDrawingUsesLineFragmentOrigin attributes:atts context:nil];
float textHeight = size.size.height;
float lines = textHeight / lineHeight;
if (lines > ) {
lines = ;
} composing = YES;
msgText.contentInset = UIEdgeInsetsMake(, , , );
sendView.frame = CGRectMake(, appFrame.size.height - keyboardHeight - - (lines * lineHeight) + lineHeight, appFrame.size.width, + (lines * lineHeight) - lineHeight); if (prevLines != lines) {
[self shortenTableView];
}
prevLines = lines;
} // let's change the frame of the chatTable so we can see the bottom
- (void)shortenTableView
{
[UIView beginAnimations:@"moveView" context:nil];
[UIView setAnimationDuration:0.1];
self.chatTable.frame = CGRectMake(, , appFrame.size.width, appFrame.size.height - keyboardHeight- - );
[UIView commitAnimations];
prevLines = ;
} // show the chatTable as it was
- (void)showTableView
{
[UIView beginAnimations:@"moveView" context:nil];
[UIView setAnimationDuration:0.1];
self.chatTable.frame = CGRectMake(, , appFrame.size.width, appFrame.size.height - - );
[UIView commitAnimations];
} // when users starts typing change the frame position and shorten the chatTable
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[UIView beginAnimations:@"moveView" context:nil];
[UIView setAnimationDuration:0.3];
sendView.frame = CGRectMake(, appFrame.size.height - keyboardHeight - , appFrame.size.width, );
[UIView commitAnimations];
[self shortenTableView];
// 变成第一响应者
[msgText becomeFirstResponder];
} - (void)addNotification
{
//键盘出现通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleKeyBoardShow:)
name:UIKeyboardWillShowNotification object:nil];
}
#pragma mark - keyboard delegate
-(void)handleKeyBoardShow:(NSNotification*)notify
{
NSDictionary *info = [notify userInfo];
// 获取键盘的尺寸
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
//获取键盘的高度
keyboardHeight = keyboardSize.height;
}
// 移除监听
-(void)removeNotification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
} - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} #pragma mark - YXYCChatTableView implementation
// here are the required implementation from your YXYCChatTableViewDataSource
- (NSInteger)rowsForChatTable:(YXYCChatTableView *)tableView
{
return [Chats count];
} - (YXYCChatData *)chatTableView:(YXYCChatTableView *)tableView dataForRow:(NSInteger)row
{
return [Chats objectAtIndex:row];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; } @end
 #import <Foundation/Foundation.h>

 @class YXYCChatData;
@class YXYCChatTableView;
@protocol YXYCChatTableViewDataSource <NSObject> - (NSInteger)rowsForChatTable:(YXYCChatTableView *)tableView;
- (YXYCChatData *)chatTableView:(YXYCChatTableView *)tableView dataForRow:(NSInteger)row; @end
 #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class YXYCChatUser;
//枚举(聊天类型)
typedef enum _YXYCChatType
{
ChatTypeMine = ,
ChatTypeSomeone =
} YXYCChatType; @interface YXYCChatData : NSObject
/**
* 聊天的对象类型
*/
@property (readonly, nonatomic) YXYCChatType type;
/**
* 日期
*/
@property (readonly, nonatomic, strong) NSDate *date;
@property (readonly, nonatomic, strong) UIView *view;
@property (readonly, nonatomic) UIEdgeInsets insets;
/**
* 聊天的用户
*/
@property (nonatomic, strong) YXYCChatUser *chatUser; // 自定义初始化
+ (id)dataWithText:(NSString *)text date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user; + (id)dataWithImage:(UIImage *)image date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user; + (id)dataWithView:(UIView *)view date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user insets:(UIEdgeInsets)insets; @end
 #import "YXYCChatData.h"
#import <QuartzCore/QuartzCore.h> @implementation YXYCChatData //设置一些文本和图片的常量
const UIEdgeInsets textInsetsMine = {, , , };
const UIEdgeInsets textInsetsSomeone = {, , , };
const UIEdgeInsets imageInsetsMine = {, , , };
const UIEdgeInsets imageInsetsSomeone = {, , , }; #pragma mark initializers
+ (id)dataWithText:(NSString *)text date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user
{
return [[YXYCChatData alloc] initWithText:text date:date type:type andUser:_user];
}
/**
* 初始化
*
* @param text 内容
* @param date 日期
* @param type 聊天对象类型
* @param _user 用户
*
* @return YXYCChatData对象
*/
- (id)initWithText:(NSString *)text date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user
{
// 设置字体
UIFont *font = [UIFont boldSystemFontOfSize:];
// 文本显示的宽,以及最大的高度
int width = , height = 10000.0;
NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
[atts setObject:font forKey:NSFontAttributeName];
// 根据文本内容返回CGRect
CGRect size = [text boundingRectWithSize:CGSizeMake(width, height) options:NSStringDrawingUsesLineFragmentOrigin attributes:atts context:nil];
// 根据返回的尺寸初始化Label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, , size.size.width, size.size.height)];
// 可多行输入
label.numberOfLines = ;
// 以单词为单位换行,以单位为单位截断
label.lineBreakMode = NSLineBreakByWordWrapping;
// text参数若存在,Label则显示text的内容;否则Label不显示文本内容
label.text = (text ? text : @"");
label.font = font;
label.backgroundColor = [UIColor clearColor];
UIEdgeInsets insets = (type == ChatTypeMine ? textInsetsMine : textInsetsSomeone);
return [self initWithView:label date:date type:type andUser:_user insets:insets];
} + (id)dataWithImage:(UIImage *)image date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user
{
return [[YXYCChatData alloc] initWithImage:image date:date type:type andUser:_user];
}
/**
* 初始化
*
* @param image 图片
* @param date 日期
* @param type 聊天对象的类型
* @param _user 用户
*
* @return YXYCChatData对象类型
*/
- (id)initWithImage:(UIImage *)image date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user
{
// 获取图片的尺寸
CGSize size = image.size;
// 如果图片的宽大于220,size的宽就等于220,size的高度等于原来的高度与原来的宽度的比例乘以220
if (size.width > ) {
size.height /= (size.width/);
size.width = ;
}
// 根据size的大小初始化imageView
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(, , size.width, size.height)];
imageView.image = image;
//设置imageView的圆角半径
imageView.layer.cornerRadius = 5.0;
imageView.layer.masksToBounds = YES;
UIEdgeInsets insets = (type == ChatTypeMine ? imageInsetsMine : imageInsetsSomeone);
return [self initWithView:imageView date:date type:type andUser:_user insets:insets];
} + (id)dataWithView:(UIView *)view date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user insets:(UIEdgeInsets)insets
{
return [[YXYCChatData alloc] initWithView:view date:date type:type andUser:(YXYCChatUser *)_user insets:insets];
}
/**
* 初始化
*
* @param view view
* @param date 日期
* @param type 聊天对象的类型
* @param _user 用户
* @param insets
*
* @return YXYCChatData对象类型
*/
- (id)initWithView:(UIView *)view date:(NSDate *)date type:(YXYCChatType)type andUser:(YXYCChatUser *)_user insets:(UIEdgeInsets)insets
{
self = [super init];
if (self) {
_chatUser = _user;
_view = view;
_date = date;
_type = type;
_insets = insets;
}
return self;
} @end
 #import <UIKit/UIKit.h>

 @interface YXYCChatHeaderTableViewCell : UITableViewCell
/**
* 日期
*/
@property (nonatomic, strong) NSDate *date;
+ (CGFloat)height; @end
 #import "YXYCChatHeaderTableViewCell.h"

 @interface  YXYCChatHeaderTableViewCell()

 @property (nonatomic, retain) UILabel *label;

 @end

 @implementation YXYCChatHeaderTableViewCell

 + (CGFloat)height
{
return 30.0;
}
/**
* 设置时间,并把时间显示在YXYCChatHeaderTableViewCell上
*/
- (void)setDate:(NSDate *)value
{
// 初始化dateFormatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
// 设置日期显示的方式
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
// 设置时间显示的方式
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *text = [dateFormatter stringFromDate:value]; if (self.label) {
self.label.text = text;
return;
}
self.selectionStyle = UITableViewCellSelectionStyleNone;
// 初始化UILabel
self.label = [[UILabel alloc] initWithFrame:CGRectMake(, , self.frame.size.width, [YXYCChatHeaderTableViewCell height])];
//设置Label的文本内容
self.label.text = text;
// 设置Label文本字体的大小
self.label.font = [UIFont boldSystemFontOfSize:];
// Label文本居中
self.label.textAlignment = NSTextAlignmentCenter;
// 设置Label文字的阴影偏余量
self.label.shadowOffset = CGSizeMake(, );
// 设置Label文字阴影的颜色
self.label.shadowColor = [UIColor whiteColor];
// 设置Label文件的颜色
self.label.textColor = [UIColor darkGrayColor];
// 设置Label的背景颜色
self.label.backgroundColor = [UIColor clearColor];
[self addSubview:self.label];
} - (void)awakeFromNib {
// Initialization code
} - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated]; // Configure the view for the selected state
} @end
 #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface YXYCChatUser : NSObject
/**
* 用户名字
*/
@property (nonatomic, strong) NSString *username;
/**
* 头像
*/
@property (nonatomic, strong) UIImage *avatar;
/**
* 初始化
*
* @param user 用户
* @param image 头像
*
* @return 返回id类型
*/
- (id)initWithUsername:(NSString *)user avatarImage:(UIImage *)image; @end
 #import "YXYCChatUser.h"

 @implementation YXYCChatUser

 @synthesize avatar;
@synthesize username; - (id)initWithUsername:(NSString *)user avatarImage:(UIImage *)image
{
self = [super init];
if (self) {
self.avatar = [image copy];
self.username = [user copy];
}
return self;
} @end
 #import <UIKit/UIKit.h>
#import "YXYCChatData.h" @interface YXYCChatTableViewCell : UITableViewCell
/**
* 日期
*/
@property (nonatomic, strong) YXYCChatData *data;
/**
* 设置日期
*/
- (void)setData:(YXYCChatData *)data; @end
 #import "YXYCChatTableViewCell.h"
#import "YXYCChatData.h"
#import <QuartzCore/QuartzCore.h>
#import "YXYCChatUser.h" @interface YXYCChatTableViewCell () @property (nonatomic, retain) UIView *customView;
@property (nonatomic, retain) UIImageView *bubbleImage;
/**
* 头像
*/
@property (nonatomic, retain) UIImageView *avatarImage; //- (void)setupInternalData; @end @implementation YXYCChatTableViewCell @synthesize data = _data; - (void)setData:(YXYCChatData *)data
{
_data = data;
[self rebuildUserInterface];
} - (void)rebuildUserInterface
{
self.selectionStyle = UITableViewCellSelectionStyleNone;
// 如果bubbleImage为空,则创建
if (!self.bubbleImage) {
self.bubbleImage = [[UIImageView alloc] init];
[self addSubview:self.bubbleImage];
}
YXYCChatType type = self.data.type;
CGFloat width = self.data.view.frame.size.width;
CGFloat height = self.data.view.frame.size.height;
CGFloat x = (type == ChatTypeSomeone) ? : self.frame.size.width - width - self.data.insets.left - self.data.insets.right;
CGFloat y = ;
// 如果用户有头像,则显示头像
if (self.data.chatUser) {
YXYCChatUser *thisUser = self.data.chatUser;
[self.avatarImage removeFromSuperview];
self.avatarImage = [[UIImageView alloc] initWithImage:(thisUser.avatar ? thisUser.avatar : [UIImage imageNamed:@"noavatar.png"])];
self.avatarImage.layer.cornerRadius = 9.0;
self.avatarImage.layer.masksToBounds = YES;
// 设置avatarImage的边框颜色
self.avatarImage.layer.borderColor = [UIColor colorWithWhite:0.0 alpha:0.2].CGColor;
// 设置avatarImage边框的粗细
self.avatarImage.layer.borderWidth = 1.0;
// 计算x的位置
CGFloat avatarX = (type == ChatTypeSomeone) ? : self.frame.size.width - ;
CGFloat avatarY = self.frame.size.height - ;
//设计frame
self.avatarImage.frame = CGRectMake(avatarX, avatarY, , );
[self addSubview:self.avatarImage];
CGFloat delta = self.frame.size.height - (self.data.insets.top + self.data.insets.bottom + self.data.view.frame.size.height);
if (delta > ) y = delta;
if (type == ChatTypeSomeone) x += ;
if (type == ChatTypeMine) x -= ;
}
[self.customView removeFromSuperview];
self.customView = self.data.view;
self.customView.frame = CGRectMake(x + self.data.insets.left, y + self.data.insets.top, width, height);
[self.contentView addSubview:self.customView];
//
if (type == ChatTypeSomeone) {
//创建一个内容可拉伸,而边角不拉伸的图片,需要两个参数,第一个是左边不拉伸区域的宽度,第二个参数是上面不拉伸的高度。
self.bubbleImage.image = [[UIImage imageNamed:@"yoububble.png"] stretchableImageWithLeftCapWidth: topCapHeight:];
}else{
self.bubbleImage.image = [[UIImage imageNamed:@"mebubble.png"] stretchableImageWithLeftCapWidth: topCapHeight:];
}
self.bubbleImage.frame = CGRectMake(x, y, width + self.data.insets.left + self.data.insets.right, height + self.data.insets.top + self.data.insets.bottom);
} //- (void)setUP:(YXYCChatData *)value
//{
// self.data = value;
// [self rebuildUserInterface];
//} - (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
[self rebuildUserInterface];
} - (void)awakeFromNib {
// Initialization code
} - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated]; // Configure the view for the selected state
} @end
 #import <UIKit/UIKit.h>
#import "YXYCChatTableViewDataSource.h"
#import "YXYCChatTableViewCell.h" typedef enum _ChatBubbleTypingType
{
ChatBubbleTypingTypeNobody = ,
ChatBubbleTypingTypeMe = ,
ChatBubbleTypingTypeSomebody =
} ChatBubbleTypingType; @interface YXYCChatTableView : UITableView
/**
* 添加代理
*/
@property (nonatomic, assign) id<YXYCChatTableViewDataSource> chatDataSource;
/**
* 时间间隔
*/
@property (nonatomic) NSTimeInterval snapInterval;
@property (nonatomic) ChatBubbleTypingType typingBubble; @end
 #import "YXYCChatTableView.h"
#import "YXYCChatData.h"
#import "YXYCChatHeaderTableViewCell.h" @interface YXYCChatTableView ()<UITableViewDelegate,UITableViewDataSource> @property (nonatomic, strong) NSMutableArray *bubbleSection; @end @implementation YXYCChatTableView - (id)init
{
self = [super init];
if (self) [self initializer];
return self;
} - (void)initializer
{
self.backgroundColor = [UIColor clearColor];
// 隐藏cell与cell之间的分离线
self.separatorStyle = UITableViewCellSeparatorStyleNone;
// 设置代理
self.delegate = self;
self.dataSource = self;
self.snapInterval = * * ;//一天的时间
self.typingBubble = ChatBubbleTypingTypeNobody;
} - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) [self initializer];
return self;
} - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
self = [super initWithFrame:frame style:UITableViewStylePlain];
if (self) {
[self initializer];
}
return self;
} #pragma mark - 重写tableView的部分方法-
- (void)reloadData
{
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.bubbleSection = nil;
int count = ;
self.bubbleSection = [[NSMutableArray alloc] init];
if (self.chatDataSource && (count = (int)[self.chatDataSource rowsForChatTable:self]) > ) {
NSMutableArray *bubbleData = [[NSMutableArray alloc] initWithCapacity:count];
for (int i = ; i < count; i++) {
NSObject *object = [self.chatDataSource chatTableView:self dataForRow:i];
assert([object isKindOfClass:[YXYCChatData class]]);
[bubbleData addObject:object];
}
// 根据时间进行排列
[bubbleData sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
YXYCChatData *bubbleData1 = (YXYCChatData *)obj1;
YXYCChatData *bubbleData2 = (YXYCChatData *)obj2;
return [bubbleData1.date compare:bubbleData2.date];
}];
// 以GMT时间的偏移秒数来初始化
NSDate *last = [NSDate dateWithTimeIntervalSince1970:];
NSMutableArray *currentSection = nil;
for (int i = ; i < count; i++) {
YXYCChatData *data = (YXYCChatData *)[bubbleData objectAtIndex:i];
// 如果时间大于一天的时间就存入self.bubbleSection(用于分区)
if ([data.date timeIntervalSinceDate:last] > self.snapInterval) {
currentSection = [[NSMutableArray alloc] init];
[self.bubbleSection addObject:currentSection];
}
[currentSection addObject:data];
last = data.date;
}
}
[super reloadData];
} - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
int result = (int)[self.bubbleSection count];
if (self.typingBubble != ChatBubbleTypingTypeNobody) {
result ++;
}
return result;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section >= [self.bubbleSection count]) {
return ;
}
return [[self.bubbleSection objectAtIndex:section] count] + ;
} - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Header
if (indexPath.row == ) {
return [YXYCChatHeaderTableViewCell height];
}
YXYCChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:indexPath.row - ];
return MAX(data.insets.top + data.view.frame.size.height + data.insets.bottom, );
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Header based on snapInterval
if (indexPath.row == ) {
static NSString *cellId = @"HeaderCell";
YXYCChatHeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
YXYCChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:];
if (cell == nil) {
cell = [[YXYCChatHeaderTableViewCell alloc] init];
}
cell.date = data.date;
return cell;
}
//Standard
static NSString * cellId = @"ChatCell";
YXYCChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
YXYCChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:indexPath.row - ];
if (cell == nil) {
cell = [[YXYCChatTableViewCell alloc] init];
}
cell.data = data;
return cell;
} @end

iOS 聊天界面的更多相关文章

  1. iOS打开手机QQ与指定用户聊天界面

    开发中遇到一个联系客服qq的需求,找到这么一个实现方法,先记录下来.大概的原理就是,iOS启动第三方应用是采用schema模式的,这有点像url,打开不同的界面使用不同的地址.但这个url怎么得来的还 ...

  2. [iOS基础控件 - 6.9] 聊天界面Demo

    A.需求 做出一个类似于QQ.微信的聊天界面 1.每个cell包含发送时间.发送人(头像).发送信息 2.使用对方头像放在左边,我方头像在右边 3.对方信息使用白色背景对话框,我方信息使用蓝色背景对话 ...

  3. Android,iOS,浏览器打开手机QQ与指定用户聊天界面

    在浏览器中可以通过JS代码打开QQ并弹出聊天界面,一般作为客服QQ使用.而在移动端腾讯貌似没有公布提供类似API,但是却可以使用schema模式来启动手机QQ. 以下为具体代码: 浏览器(包括手机浏览 ...

  4. 【转】Android,iOS打开手机QQ与指定用户聊天界面

    在浏览器中可以通过JS代码打开QQ并弹出聊天界面,一般作为客服QQ使用.而在移动端腾讯貌似没有公布提供类似API,但是却可以使用schema模式来启动手机QQ. 以下为具体代码: Android: S ...

  5. iOS浏览器不能打开手机QQ客服与指定用户聊天界面

    这个问题是我在公司需求的时候遇到的,QQ推广工具网站获取的链接在苹果自带浏览器没法打开到聊天界面,是因为safair在打开到app store的时候把参数给丢了,app store再打开到QQ的时候就 ...

  6. Android,iOS打开手机QQ与指定用户聊天界面

    在浏览器中能够通过JS代码打开QQ并弹出聊天界面.一般作为客服QQ使用. 而在移动端腾讯貌似没有发布提供相似API,可是却能够使用schema模式来启动手机QQ. 下面为详细代码: Android: ...

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

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

  8. QQ聊天界面的布局和设计(IOS篇)-第二季

    QQChat Layout - 第二季 本来第二季是快写好了, 也花了点功夫, 结果gitbook出了点问题, 给没掉了.有些细节可能会一带而过, 如有疑问, 相互交流进步~. 在第一季中我们完成了Q ...

  9. QQ聊天界面的布局和设计(IOS篇)-第一季

    我写的源文件整个工程会再第二季中发上来~,存在百度网盘, 感兴趣的童鞋, 可以关注我的博客更新,到时自己去下载~.喵~~~ QQChat Layout - 第一季 一.准备工作 1.将假数据messa ...

随机推荐

  1. tableau 常识积累

    没怎么在业务系统中使用过,所以需要好好积累.看起来很简单的东西都需要慢慢来用.下了一份它的官方文档10.3版本的.公司网络限制,不能去它官网学习.只有下班时间了. 先说一个,有时候度量值它和HANA类 ...

  2. mysql 逻辑查询语句执行顺序

    一 SELECT语句关键字的定义顺序 SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOI ...

  3. EmbarrassedBird网站

    试想现在有如下情景 (情景1) 你写了一封情书准备给心中暗恋很久很久的小Z同学, 我们假设, 你提起来超级无敌巨大的勇气把情书直接交给了小Z, 现在有两种情况 a. 小Z也喜欢你, 欢乐大结局! b. ...

  4. 如何卸载Python通过setup.py安装的模块

    0,pip uninstall  xxxx     1.找到egg sudo easy_install -m BitVector .... Using /usr/local/lib/python2./ ...

  5. Js中的prototype的用法一

    一 prototype介绍 prototype对象是实现面向对象的一个重要机制.每个函数也是一个对象,它们对应的类就是function,每个函数对象都具有一个子对象prototype.Prototyp ...

  6. angular 的杂碎报错小知识

    1:[ng:areq] Angular出现这种错误的原因,是由于没有在页面中使用模块引入controller导致的 所以 请确保你定义了这个controller后也引用了它. 2:Failed to ...

  7. 遍历js的obj中所有属性得key

    var obj = $("#jstree_default").jstree("get_checked"); for (var a in obj) { alert ...

  8. #关于 OneVsRestClassifier(LogisticRegression(太慢了,要用超过的机器)

    #关于 OneVsRestClassifier #注意以下代码中,有三个类 from sklearn import datasets X, y = datasets.make_classificati ...

  9. openGL一些概念02

    着色器程序 着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本. 如果要使用刚才编译的着色器我们必须把他们链接为一个着色器程序对象,然后在渲染对象的时 ...

  10. DAY10-MYSQL库操作

    一 系统数据库 information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息.列信息.权限信息.字符信息等performance_schema: MyS ...