环信2

任务


1.完善输入框


2.发语音


3.发图片


4.聊天的时间显示


5.历史的会话记录


01.完善聊天输入框



打开  XMGChatViewController.m


在延展中加入


/** InputToolBar 高度的约束*/

@property (weak,nonatomic) IBOutlet NSLayoutConstraint *inputToolBarHegihtConstraint;


修改 textViewDidChange  方法如下:


#pragma mark - UITextView代理

-(void)textViewDidChange:(UITextView *)textView{

    

    NSLog(@"contentOffset %@",NSStringFromCGPoint(textView.contentOffset));

    // 1.计算TextView的高度,

    CGFloat textViewH = 0;

    CGFloat minHeight = 33;//textView最小的高度

    CGFloat maxHeight = 68;//textView最大的高度

    

    // 获取contentSize的高度

    CGFloat contentHeight = textView.contentSize.height;

    if (contentHeight < minHeight) {

        textViewH = minHeight;

    }elseif (contentHeight > maxHeight){

        textViewH = maxHeight;

    }else{

        textViewH = contentHeight;

    }

    

    

    

    // 2.监听Send事件--判断最后的一个字符是不是换行字符

    if ([textView.text hasSuffix:@"\n"]) {

        NSLog(@"发送操作");

        [self sendMessage:textView.text];

        

        // 清空textView的文字

        textView.text = nil;

        

        // 发送时,textViewH的高度为33

        textViewH = minHeight;

        

    }

    

    // 3.调整整个InputToolBar高度

    self.inputToolBarHegihtConstraint.constant =6 + 7 + textViewH;

    // 加个动画

    [UIView animateWithDuration:0.25 animations:^{

        [self.view layoutIfNeeded];

    }];

    

    

    // 4.记光标回到原位

#warning 技巧

    [textView setContentOffset:CGPointZero animated:YES];

    [textView scrollRangeToVisible:textView.selectedRange];

}



打开  Main.storyboard


找到  XMGChatViewController 控制器


选中 ChatInputToolBar  找到其约束  Heigh Equals46 连接输出口 inputToolBarHegihtConstraint



  ChatInputToolBar  视图 中添加  imageView 添加到第一个控件位置


设置  label:textViewBg

image:chatBar_textBg

Stretching   x  y   width  height:0.5 0.5  0 0


x  y   width  height:43 6  196  33


设置 约束   勾选  上下左右



选中  textView   background:default


运行




02.发送语音


在录音前导入环信封装的两个录音框架


参看PPT-22


iOSSDK-20160202/ChatDemo-UI2.0/3rdparty下面的  DeviceUtilVoiceConvert添加到工程lib目录下


刚才导入的两个框架,已经实现了录音Api



打开  XMGChatViewController.m


加入  #import"EMCDDeviceManager.h"


在延展中加入


@property (weak,nonatomic) IBOutlet UIButton *recordBtn;



在类中加入方法



#pragma mark 发送语音消息

-(void)sendVoice:(NSString *)recordPath duration:(NSInteger)duration{

    // 1.构造一个语音消息体

    EMChatVoice *chatVoice = [[EMChatVoice alloc] initWithFile:recordPath displayName:@"[语音]"];

    //    chatVoice.duration = duration;

    

    EMVoiceMessageBody *voiceBody = [[EMVoiceMessageBody alloc] initWithChatObject:chatVoice];

    voiceBody.duration = duration;

    

    // 2.构造一个消息对象

    EMMessage *msgObj = [[EMMessage alloc] initWithReceiver:self.buddy.username bodies:@[voiceBody]];

    //聊天的类型单聊

    msgObj.messageType = eMessageTypeChat;

    

    // 3.发送

    [[EaseMob sharedInstance].chatManager asyncSendMessage:msgObj progress:nil prepare:^(EMMessage *message, EMError *error) {

        NSLog(@"准备发送语音");

        

    } onQueue:nil completion:^(EMMessage *message, EMError *error) {

        if (!error) {

            NSLog(@"语音发送成功");

        }else{

            NSLog(@"语音发送失败");

        }

    } onQueue:nil];

    

    // 3.把消息添加到数据源,然后再刷新表格

    [self.dataSources addObject:msgObj];

    [self.tableView reloadData];

    // 4.把消息显示在顶部

    [self scrollToBottom];

    

    

    

    

}




#pragma mark - Action


- (IBAction)voiceAction:(UIButton *)sender {

    

    // 1.显示录音按钮

    self.recordBtn.hidden = !self.recordBtn.hidden;

    

}

#pragma mark 按钮点下去开始录音

- (IBAction)beginRecordAction:(id)sender {

    // 文件名以时间命名

    int x = arc4random() %100000;

    NSTimeInterval time = [[NSDate date] timeIntervalSince1970];

    NSString *fileName = [NSString stringWithFormat:@"%d%d",(int)time,x];

    

    NSLog(@"按钮点下去开始录音");

    [[EMCDDeviceManager sharedInstance] asyncStartRecordingWithFileName:fileName completion:^(NSError *error) {

        if (!error) {

            NSLog(@"开始录音成功");

        }

    }];

}


#pragma mark 手指从按钮范围内松开结束录音

- (IBAction)endRecordAction:(id)sender {

    NSLog(@"手指从按钮松开结束录音");

    [[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {

        if (!error) {

            NSLog(@"录音成功");

            NSLog(@"%@",recordPath);

            // 发送语音给服务器

            [self sendVoice:recordPath duration:aDuration];

            

        }else{

            NSLog(@"== %@",error);

            

        }

    }];

}

#pragma mark 手指从按钮外面松开取消录音

- (IBAction)cancelRecordAction:(id)sender {

    [[EMCDDeviceManager sharedInstance] cancelCurrentRecording];

    

}


打开  Main.storyboard


找到  XMGChatViewController 控制器


  ChatInputToolBar  视图 中添加 button   title:按住说话

label:recordBtn

Text Color:White Color

Background: Dark Gray Color

State Config  :  Highlighted   松开发送

x  y   width  height:43 5  196  33

约束 勾选  左右下  5  5 8

勾选   Height:33


勾选  Hidden




连接输出口  recordBtn


事件


beginRecordAction  —  recordBtn  (Touch Down)

cancelRecordAction  —  recordBtn  (Touch Up Outside)

endRecordAction    —  recordBtn  (Touch Up Inside)

voiceAction        —   recordBtn  (Touch Up Inside)



运行


查看沙盒Library/appdata/chatbuffer下是否生成 .amr文件





03.播放语音


右键   Chat【聊天】  — new  class  name:XMGAudioPlayTool

superclass:NSObject


打开  XMGAudioPlayTool.h


加入 #import"EaseMob.h"


在加入


+(void)playWithMessage:(EMMessage *)msg msgLabel:(UILabel *)msgLabel receiver:(BOOL)receiver;




打开  XMGAudioPlayTool.m


加入


#import "EMCDDeviceManager.h"


static UIImageView *animatingImageView;//正在执行动画的ImageView


在加入


+(void)playWithMessage:(EMMessage *)msg msgLabel:(UILabel *)msgLabel receiver:(BOOL)receiver{

    

    // 把以前的动画移除

    [animatingImageView stopAnimating];

    [animatingImageView removeFromSuperview];

    

    //1.播放语音

    //获取语音路径

    EMVoiceMessageBody *voiceBody = msg.messageBodies[0];

    

    // 本地语音文件路径

    NSString *path = voiceBody.localPath;

    

    // 如果本地语音文件不存在,使用服务器语音

    NSFileManager *manager = [NSFileManager defaultManager];

    if (![manager fileExistsAtPath:path]) {

        path = voiceBody.remotePath;

    }

    

    [[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:path completion:^(NSError *error) {

        

        NSLog(@"语音播放完成 %@",error);

        // 移除动画

        [animatingImageView stopAnimating];

        [animatingImageView removeFromSuperview];

        

        

    }];

    

    //2.添加动画

    //2.1创建一个UIImageView添加到Label

    UIImageView *imgView = [[UIImageView alloc] init];

    [msgLabel addSubview:imgView];

    

    //2.2添加动画图片

    if (receiver) {

        imgView.animationImages = @[[UIImage imageNamed:@"chat_receiver_audio_playing000"],

                                    [UIImage imageNamed:@"chat_receiver_audio_playing001"],

                                    [UIImage imageNamed:@"chat_receiver_audio_playing002"],

                                    [UIImage imageNamed:@"chat_receiver_audio_playing003"]];

        imgView.frame = CGRectMake(0,0, 30,30);

    }else{

        imgView.animationImages = @[[UIImage imageNamed:@"chat_sender_audio_playing_000"],

                                    [UIImage imageNamed:@"chat_sender_audio_playing_001"],

                                    [UIImage imageNamed:@"chat_sender_audio_playing_002"],

                                    [UIImage imageNamed:@"chat_sender_audio_playing_003"]];

        

        imgView.frame = CGRectMake(msgLabel.bounds.size.width - 30, 0,30, 30);

    }

    

    imgView.animationDuration = 1;

    [imgView startAnimating];

    animatingImageView = imgView;

    

}








打开  XMGChatCell.m



加入


#import "EMCDDeviceManager.h"

#import "XMGAudioPlayTool.h"


修改  setMessage 方法如下


-(void)awakeFromNib{

    // 在此方法做一些初始化操作

    // 1.label添加敲击手势

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(messageLabelTap:)];

    [self.messageLabel addGestureRecognizer:tap];

    

}


#pragma mark messagelabel 点击的触发方法

-(void)messageLabelTap:(UITapGestureRecognizer *)recognizer{

    NSLog(@"%s",__func__);

    //播放语音

    //只有当前的类型是为语音的时候才播放

    //1.获取消息体

    id body =self.message.messageBodies[0];

    if ([body isKindOfClass:[EMVoiceMessageBodyclass]]) {

        NSLog(@"播放语音");

        BOOL receiver = [self.reuseIdentifier isEqualToString:ReceiverCell];

        [XMGAudioPlayTool playWithMessage:self.message msgLabel:self.messageLabel receiver:receiver];

        

    }

    //    EMTextMessageBody

    //    EMVoiceMessageBody

    //    EMImageMessageBody

    

    

}


-(void)setMessage:(EMMessage *)message{

    

    _message = message;

    

    // 1.获取消息体

    id body = message.messageBodies[0];

    if ([body isKindOfClass:[EMTextMessageBodyclass]]) {//文本消息

        EMTextMessageBody *textBody = body;

        self.messageLabel.text = textBody.text;

    }elseif([body isKindOfClass:[EMVoiceMessageBodyclass]]){//语音消息

        //        self.messageLabel.text = @"【语音】";

        self.messageLabel.attributedText = [self voiceAtt];

    }

    else{

        self.messageLabel.text =@"未知类型";

    }

    

    

}


#pragma mark 返回语音富文本

-(NSAttributedString *)voiceAtt{

    // 创建一个可变的富文本

    NSMutableAttributedString *voiceAttM = [[NSMutableAttributedString alloc] init];

    

    // 1.接收方:富文本图片 +时间

    if ([self.reuseIdentifier isEqualToString:ReceiverCell]) {

        // 1.1接收方的语音图片

        UIImage *receiverImg = [UIImage imageNamed:@"chat_receiver_audio_playing_full"];

        

        // 1.2创建图片附件

        NSTextAttachment *imgAttachment = [[NSTextAttachment alloc] init];

        imgAttachment.image = receiverImg;

        imgAttachment.bounds = CGRectMake(0, -7,30, 30);

        // 1.3图片富文本

        NSAttributedString *imgAtt = [NSAttributedString attributedStringWithAttachment:imgAttachment];

        [voiceAttM appendAttributedString:imgAtt];

        

        // 1.4.创建时间富文本

        // 获取时间

        EMVoiceMessageBody *voiceBody = self.message.messageBodies[0];

        NSInteger duration = voiceBody.duration;

        NSString *timeStr = [NSString stringWithFormat:@"%ld'",duration];

        NSAttributedString *timeAtt = [[NSAttributedString alloc] initWithString:timeStr];

        [voiceAttM appendAttributedString:timeAtt];

        

    }else{

        // 2.发送方:富文本时间 +图片

        // 2.1 拼接时间

        // 获取时间

        EMVoiceMessageBody *voiceBody = self.message.messageBodies[0];

        NSInteger duration = voiceBody.duration;

        NSString *timeStr = [NSString stringWithFormat:@"%ld'",duration];

        NSAttributedString *timeAtt = [[NSAttributedString alloc] initWithString:timeStr];

        [voiceAttM appendAttributedString:timeAtt];

        

        

        // 2.1拼接图片

        UIImage *receiverImg = [UIImage imageNamed:@"chat_sender_audio_playing_full"];

        

        // 创建图片附件

        NSTextAttachment *imgAttachment = [[NSTextAttachment alloc] init];

        imgAttachment.image = receiverImg;

        imgAttachment.bounds = CGRectMake(0, -7,30, 30);

        // 图片富文本

        NSAttributedString *imgAtt = [NSAttributedString attributedStringWithAttachment:imgAttachment];

        [voiceAttM appendAttributedString:imgAtt];

        

    }

    

    return [voiceAttM copy];

    

}




打开   XMGChatViewController.m


在延展中加入


@property (weak,nonatomic) IBOutlet UITextView *textView;




修改  voiceAction  方法如下


- (IBAction)voiceAction:(UIButton *)sender {

    

    // 1.显示录音按钮

    self.recordBtn.hidden = !self.recordBtn.hidden;

    

    if (self.recordBtn.hidden ==NO) {//录音按钮要显示

        //InputToolBar 的高度要回来默认(46);

        self.inputToolBarHegihtConstraint.constant =46;

        // 隐藏键盘

        [self.view endEditing:YES];

    }else{

        //当不录音的时候,键盘显示

        [self.textView becomeFirstResponder];

        

        // 恢复InputToolBar高度

        [self textViewDidChange:self.textView];

    }

    

}




sendMessage 方法顶上加入


#pragma mark 发送文本消息





打开  Main.storyboard


找到  XMGChatViewController 控制器


连接  textView 输出口



运行






04.发送图片显示图片


将课件中的  SDWebImage  添加到工程Lib目录下


打开  XMGChatCell.m


加入  #import"UIImageView+WebCache.h"


setMessage方法中的


else if([body isKindOfClass:[EMVoiceMessageBodyclass]]){//语音消息

    //        self.messageLabel.text = @"【语音】";

    self.messageLabel.attributedText = [self voiceAtt];

}


下面加入


else if([body isKindOfClass:[EMImageMessageBodyclass]]){//图片消息

    [self showImage];

}



在类中加入方法


-(void)showImage{

    

    // 获取图片消息体

    EMImageMessageBody *imgBody = self.message.messageBodies[0];

    CGRect thumbnailFrm = (CGRect){0,0,imgBody.thumbnailSize};

    

    // 设置Label的尺寸足够显示UIImageView

    NSTextAttachment *imgAttach = [[NSTextAttachment alloc] init];

    imgAttach.bounds = thumbnailFrm;

    NSAttributedString *imgAtt = [NSAttributedString attributedStringWithAttachment:imgAttach];

    self.messageLabel.attributedText = imgAtt;

    

    //1.cell里添加一个UIImageView

    UIImageView *chatImgView = [[UIImageView alloc] init];

    [self.messageLabel addSubview:chatImgView];

    chatImgView.backgroundColor = [UIColor redColor];

    

    //2.设置图片控件为缩略图的尺寸

    chatImgView.frame = thumbnailFrm;

    

    //3.下载图片

    NSLog(@"thumbnailLocalPath %@",imgBody.thumbnailLocalPath);

    NSLog(@"thumbnailRemotePath %@",imgBody.thumbnailRemotePath);

    NSFileManager *manager = [NSFileManager defaultManager];

    // 如果本地图片存在,直接从本地显示图片

    UIImage *palceImg = [UIImage imageNamed:@"downloading"];

    if ([manager fileExistsAtPath:imgBody.thumbnailLocalPath]) {

#warning 本地路径使用fileURLWithPath方法

        [chatImgView sd_setImageWithURL:[NSURL fileURLWithPath:imgBody.thumbnailLocalPath] placeholderImage:palceImg];

    }else{

        // 如果本地图片不存,从网络加载图片

        [chatImgView sd_setImageWithURL:[NSURL URLWithString:imgBody.thumbnailRemotePath] placeholderImage:palceImg];

    }

    

    

}







打开   XMGChatViewController.m


在延展中加入


,UIImagePickerControllerDelegate,UINavigationControllerDelegate



在类中加入方法


#pragma mark 发送图片

-(void)sendImg:(UIImage *)selectedImg{

    

    //1.构造图片消息体

    /*

     * 第一个参数:原始大小的图片对象 1000 * 1000

     * 第二个参数:缩略图的图片对象  120 * 120

     */

    EMChatImage *orginalChatImg = [[EMChatImage alloc] initWithUIImage:selectedImg displayName:@"【图片】"];

    

    EMImageMessageBody *imgBody = [[EMImageMessageBody alloc] initWithImage:orginalChatImg thumbnailImage:nil];

    

    [self sendMessage:imgBody];

    

}


-(void)sendMessage:(id<IEMMessageBody>)body{

    //1.构造消息对象

    EMMessage *msgObj = [[EMMessage alloc] initWithReceiver:self.buddy.username bodies:@[body]];

    msgObj.messageType = eMessageTypeChat;

    

    //2.发送消息

    [[EaseMob sharedInstance].chatManager asyncSendMessage:msgObj progress:nil prepare:^(EMMessage *message, EMError *error) {

        NSLog(@"准备发送图片");

    } onQueue:nil completion:^(EMMessage *message, EMError *error) {

        NSLog(@"图片发送成功 %@",error);

    } onQueue:nil];

    

    // 3.把消息添加到数据源,然后再刷新表格

    [self.dataSources addObject:msgObj];

    [self.tableView reloadData];

    // 4.把消息显示在顶部

    [self scrollToBottom];

    

}






sendMessage方法名字改为   sendText


textViewDidChange 方法中的    sendMessage方法名字改为   sendText





修改 sendTextsendVoice 2个方法如下



#pragma mark 发送文本消息

-(void)sendText:(NSString *)text{

    

    // 把最后一个换行字符去除

#warning 换行字符只占用一个长度

    text = [text substringToIndex:text.length - 1];

    

    // 创建一个聊天文本对象

    EMChatText *chatText = [[EMChatText alloc] initWithText:text];

    

    //创建一个文本消息体

    EMTextMessageBody *textBody = [[EMTextMessageBody alloc] initWithChatObject:chatText];

    

    [self sendMessage:textBody];

    

}


#pragma mark 发送语音消息

-(void)sendVoice:(NSString *)recordPath duration:(NSInteger)duration{

    // 1.构造一个语音消息体

    EMChatVoice *chatVoice = [[EMChatVoice alloc] initWithFile:recordPath displayName:@"[语音]"];

    //    chatVoice.duration = duration;

    

    EMVoiceMessageBody *voiceBody = [[EMVoiceMessageBody alloc] initWithChatObject:chatVoice];

    voiceBody.duration = duration;

    

    [self sendMessage:voiceBody];

}






在加入方法


- (IBAction)showImgPickerAction:(id)sender {

    //显示图片选择的控制器

    UIImagePickerController *imgPicker = [[UIImagePickerController alloc] init];

    

    // 设置源

    imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

    imgPicker.delegate = self;

    

    [self presentViewController:imgPicker animated:YES completion:NULL];

}


/**用户选中图片的回调*/

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{

    

    //1.获取用户选中的图片

    UIImage *selectedImg =  info[UIImagePickerControllerOriginalImage];

    

    //2.发送图片

    [self sendImg:selectedImg];

    

    //3.隐藏当前图片选择控制器

    [self dismissViewControllerAnimated:YES completion:NULL];

    

}





打开  Main.storyboard


找到  XMGChatViewController 控制器


连接 moreBtn按钮   showImgPickerAction 事件





运行


查看沙盒 Library/appdata/Chat/lamcoChat/messages下面是否有图片





05.显示时间的cell


打开  XMGAudioPlayTool.h


加入  +(void)stop;



打开  XMGAudioPlayTool.h


加入方法


+(void)stop{

    //停止播放语音

    [[EMCDDeviceManager sharedInstance] stopPlaying];

    //移除动画

    [animatingImageView stopAnimating];

    [animatingImageView removeFromSuperview];

}



打开   XMGChatCell.m



加入


@interface XMGChatCell()

/** 聊天图片控件 */

@property (nonatomic,strong) UIImageView *chatImgView;


@end



在类中加入方法


-(UIImageView *)chatImgView{

    if (!_chatImgView) {

        _chatImgView = [[UIImageView alloc] init];

    }

    

    return _chatImgView;

}



  setMessage 方法第一句位置加入


//重用时,把聊天图片控件移除

[self.chatImgView removeFromSuperview];



  showImage方法中的


//1.cell里添加一个UIImageView

UIImageView *chatImgView = [[UIImageView alloc] init];

[self.messageLabel addSubview:chatImgView];

chatImgView.backgroundColor = [UIColor redColor];



改为

//1.cell里添加一个UIImageView

[self.messageLabel addSubview:self.chatImgView];


  chatImgView.frame = thumbnailFrm;


改为  self.chatImgView.frame = thumbnailFrm;


if ([manager fileExistsAtPath:imgBody.thumbnailLocalPath]) {

#warning 本地路径使用fileURLWithPath方法

    [chatImgView sd_setImageWithURL:[NSURL fileURLWithPath:imgBody.thumbnailLocalPath] placeholderImage:palceImg];

}else{

    // 如果本地图片不存,从网络加载图片

    [chatImgView sd_setImageWithURL:[NSURL URLWithString:imgBody.thumbnailRemotePath] placeholderImage:palceImg];

}



改为


if ([manager fileExistsAtPath:imgBody.thumbnailLocalPath]) {

#warning 本地路径使用fileURLWithPath方法

    [self.chatImgView sd_setImageWithURL:[NSURL fileURLWithPath:imgBody.thumbnailLocalPath] placeholderImage:palceImg];

}else{

    // 如果本地图片不存,从网络加载图片

    [self.chatImgView sd_setImageWithURL:[NSURL URLWithString:imgBody.thumbnailRemotePath] placeholderImage:palceImg];

}




右键   Chat【聊天】  … new  class  name:XMGTimeCell

superclass:UITableViewCell


打开  XMGTimeCell.h


加入 @property (weak,nonatomic) IBOutlet UILabel *timeLabel;





说明:


时间显示的规则

同一分中内的消息,只显示一个时间

/*

 15:52

 msg1 15:52:10

 msg2 15:52:08

 msg2 15:52:02

 */


/*今天:时: (HH:mm)

 *昨天:昨天 + + (昨天 HH:mm)

 *昨天以前:(前天)年:月:日分(2015-09-26 15:27

 */






打开  XMGChatViewController.m


加入


#import "XMGAudioPlayTool.h"

#import "XMGTimeCell.h"




  viewDidLoad 方法中的


[super viewDidLoad];


下面加入


//设置背景颜色

self.tableView.backgroundColor = [UIColor colorWithRed:246/255.0 green:246/255.0 blue:246/255.0 alpha:1];



  loadLocalChatRecords 方法中的第一句位置加入


//假设在数组的第一位置添加时间

[self.dataSources addObject:@"16:06"];



heightForRowAtIndexPath


//时间cell的高度是固定

if ([self.dataSources[indexPath.row] isKindOfClass:[NSStringclass]]) {

    return18;

}


cellForRowAtIndexPath 方法中的第一句位置加入


//判断数据源类型

if ([self.dataSources[indexPath.row] isKindOfClass:[NSStringclass]]) {//显示时间cell

    XMGTimeCell *timeCell = [tableView dequeueReusableCellWithIdentifier:@"TimeCell"];

    timeCell.timeLabel.text = self.dataSources[indexPath.row];

    return timeCell;

    

}


  voiceAction  方法中的


self.recordBtn.hidden = !self.recordBtn.hidden;下面加入


self.textView.hidden = !self.textView.hidden;






在加入方法


-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{

    //停止语音播放

    [XMGAudioPlayTool stop];

}





打开  Main.storyboard


找到  XMGChatViewController 控制器


清除掉  ReceiverCellSenderCell 2个单元格的背景色  clear color


选中  table view    Separator:None


  Table View 中添加  cell    class:XMGTimeCell

label:TimeCell

Identifier:TimeCell

background: Light Gray  Color

row height:18


  TimeCell  中添加  label       font:13


设置约束   点击右下角第一个 勾选   Horizontallyin Container

勾选   Vertically   in   Container


连接   timeLabel输出口


运行





06.显示时间计算


右键   Chat【聊天】   —new  class  name:XMGTimeTool

superclass:NSObject




打开 XMGTimeTool.h


加入


//时间戳

+(NSString *)timeStr:(longlong)timestamp;



打开 XMGTimeTool.m


加入  #import <CoreGraphics/CoreGraphics.h>


在加入



+(NSString *)timeStr:(longlong)timestamp{

    //返回时间格式

    

    

    //currentDate 2015-09-28 16:28:09 +0000

    //msgDate 2015-09-28 10:36:22 +0000

    NSCalendar   *calendar = [NSCalendar currentCalendar];

    //1.获取当前的时间

    NSDate *currentDate = [NSDate date];

    

    // 获取年,月,日

    NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:currentDate];

    NSInteger currentYear = components.year;

    NSInteger currentMonth = components.month;

    NSInteger currentDay = components.day;

    NSLog(@"currentYear %ld",components.year);

    NSLog(@"currentMonth %ld",components.month);

    NSLog(@"currentDay %ld",components.day);

    

    

    //2.获取消息发送时间

    NSDate *msgDate = [NSDate dateWithTimeIntervalSince1970:timestamp/1000.0];

    // 获取年,月,日

    components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:msgDate];

    CGFloat msgYead = components.year;

    CGFloat msgMonth = components.month;

    CGFloat msgDay = components.day;

    NSLog(@"msgYear %ld",components.year);

    NSLog(@"msgMonth %ld",components.month);

    NSLog(@"msgDay %ld",components.day);

    

    

    //3.判断:

    /*今天:(HH:mm)

     *昨天: (昨天 HH:mm)

     *昨天以前:2015-09-26 15:27

     */

    NSDateFormatter *dateFmt = [[NSDateFormatter alloc] init];

    if (currentYear == msgYead

        && currentMonth == msgMonth

        && currentDay == msgDay) {//今天

        dateFmt.dateFormat= @"HH:mm";

    }elseif(currentYear == msgYead

             && currentMonth == msgMonth

             && currentDay - 1 == msgDay){//昨天

        dateFmt.dateFormat= @"昨天 HH:mm";

    }else{//昨天以前

        dateFmt.dateFormat= @"yyy-MM-dd HH:mm";

    }

    

    

    return [dateFmt stringFromDate:msgDate];

}



打开   XMGChatViewController.m


加入  #import"XMGTimeTool.h"


在延展中加入


/** 当前添加的时间 */

@property (nonatomic,copy) NSString *currentTimeStr;



在类中加入方法


-(void)addDataSourcesWithMessage:(EMMessage *)msg{

    // 1.判断EMMessage对象前面是否要加 "时间"

    //    if (self.dataSources.count == 0) {

            long long timestamp = ([[NSDate date] timeIntervalSince1970] - 60 * 60 * 24 * 2) * 1000;

    //

    //    }

    

    NSString *timeStr = [XMGTimeTool timeStr:msg.timestamp];

    if (![self.currentTimeStr isEqualToString:timeStr]) {

        [self.dataSources addObject:timeStr];

        self.currentTimeStr = timeStr;

    }

    

    

    // 2.再加EMMessage

    [self.dataSources addObject:msg];

    

}




修改 loadLocalChatRecords 方法如下


-(void)loadLocalChatRecords{

    //假设在数组的第一位置添加时间

    //    [self.dataSources addObject:@"16:06"];

    

    // 要获取本地聊天记录使用会话对象

    EMConversation *conversation = [[EaseMob sharedInstance].chatManager conversationForChatter:self.buddy.username conversationType:eConversationTypeChat];

    

    // 加载与当前聊天用户所有聊天记录

    NSArray *messages = [conversation loadAllMessages];

    

    //    for (id obj in messages) {

    //        NSLog(@"%@",[obj class]);

    //    }

    

    // 添加到数据源

    //    [self.dataSources addObjectsFromArray:messages];

    for (EMMessage *msgObjin messages) {

        [self addDataSourcesWithMessage:msgObj];

    }

}




  sendMessage 方法中的


[self.dataSources addObject:msgObj];


改为


[self addDataSourcesWithMessage:msgObj];



运行





07.显示历史会话记录


通过沙盒找到数据库文件 查看里面的表  Conversation  历史会话记录表


打开   XMGConversationViewController.m


在延展中加入


/** 历史会话记录 */

@property (nonatomic,strong) NSArray *conversations;




  viewDidLoad 方法中的


//设置代理

[[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];



下面加入


//获取历史会话记录

[self loadConversations];



在类中加入方法


-(void)loadConversations{

    //获取历史会话记录

    //1.从内存获取历史会话记录

    NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];

    

    

    //2.如果内存里没有会话记录,从数据库Conversation

    if (conversations.count ==0) {

        conversations =  [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];

    }

    

    NSLog(@"zzzzzzz %@",conversations);

    self.conversations = conversations;

    

    //显示总的未读数

    [self showTabBarBadge];

}



-(void)showTabBarBadge{

    //遍历所有的会话记录,将未读取的消息数进行累

    

    NSInteger totalUnreadCount = 0;

    for (EMConversation *conversationin self.conversations) {

        totalUnreadCount += [conversation unreadMessagesCount];

    }

    

    self.navigationController.tabBarItem.badgeValue = [NSString stringWithFormat:@"%ld",totalUnreadCount];

    

}




修改  numberOfSectionsInTableViewnumberOfRowsInSection2个方法如下


#pragma mark - Table view data source


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

#warning Incomplete method implementation.

    // Return the number of rows in the section.

    returnself.conversations.count;

}


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *ID = @"ConversationCell";

    

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    

    //获取会话模型

    EMConversation *conversaion = self.conversations[indexPath.row];

    

    // 显示数据

    // 1.显示用户名

    cell.textLabel.text = [NSString stringWithFormat:@"%@ ====未读消息数:%ld",conversaion.chatter,[conversaion unreadMessagesCount]];

    

    // 2.显示最新的一条记录

    // 获取消息体

    id body = conversaion.latestMessage.messageBodies[0];

    if ([body isKindOfClass:[EMTextMessageBodyclass]]) {

        EMTextMessageBody *textBody = body;

        cell.detailTextLabel.text = textBody.text;

    }elseif ([body isKindOfClass:[EMVoiceMessageBodyclass]]){

        EMVoiceMessageBody *voiceBody = body;

        cell.detailTextLabel.text = [voiceBody displayName];

    }elseif([body isKindOfClass:[EMImageMessageBodyclass]]){

        EMImageMessageBody *imgBody = body;

        cell.detailTextLabel.text = imgBody.displayName;

    }else{

        cell.detailTextLabel.text = @"未知消息类型";

    }

    

    return cell;

    

    

}


在加入方法


#pragma mark 接收好友的添加请求

-(void)didReceiveBuddyRequest:(NSString *)username message:(NSString *)message{

    

    // 赋值

    self.buddyUsername = username;

    

    // 对话框

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"好友添加请求" message:message delegate:self cancelButtonTitle:@"拒绝" otherButtonTitles:@"同意",nil];

    [alert show];

    

}



-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

    

    if (buttonIndex ==0) {//拒绝好友请求

        [[EaseMob sharedInstance].chatManager rejectBuddyRequest:self.buddyUsername reason:@"我不认识你" error:nil];

    }else{//同意好友请求

        [[EaseMob sharedInstance].chatManager acceptBuddyRequest:self.buddyUsername error:nil];

        

    }

}



#pragma mark 监听被好友删除

-(void)didRemovedByBuddy:(NSString *)username{

    

    NSString *message = [username stringByAppendingString:@"把你删除"];

    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"xxxx" message:message delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil];

    [alert show];

    

    

}


在加入方法


#pragma mark 历史会话列表更新

-(void)didUpdateConversationList:(NSArray *)conversationList{

    

    //给数据源重新赋值

    self.conversations = conversationList;

    

    //刷新表格

    [self.tableView reloadData];

    

    //显示总的未读数

    [self showTabBarBadge];

    

}


#pragma mark 未读消息数改变

- (void)didUnreadMessagesCountChanged{

    //更新表格

    [self.tableView reloadData];

    //显示总的未读数

    [self showTabBarBadge];

    

}



打开  XMGChatViewController.m


didReceiveMessage 方法中的


[self.dataSources addObject:message];


改为


[self addDataSourcesWithMessage:message];






打开   Main.storyboard


找到  XMGConversationViewController 控制器


选中  UITableView Cell     Identifier:ConversationCell



运行



08.设置消息为已读



历史的会话记录(EMConverstion)


EMConverstion

1.从数据库获取聊天记录

2.设置消息为已读



打开  XMGConversationViewController.m


加入  #import"XMGChatViewController.h"


在类中加入方法


-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    //进入到聊天控制器

    //1.storybaord加载聊天控制器

    XMGChatViewController *chatVc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"ChatPage"];

    //会话

    EMConversation *conversation = self.conversations[indexPath.row];

    EMBuddy *buddy = [EMBuddy buddyWithUsername:conversation.chatter];

    //2.设置好友属性

    chatVc.buddy = buddy;

    

    //3.展现聊天界面

    [self.navigationController pushViewController:chatVc animated:YES];

    

    

}




打开  XMGChatViewController.m


在延展中加入


/** 当前会话对象 */

@property (nonatomic,strong) EMConversation *conversation;



  loadLocalChatRecords 方法中的


// 要获取本地聊天记录使用会话对象

EMConversation *conversation = [[EaseMob sharedInstance].chatManager conversationForChatter:self.buddy.username conversationType:eConversationTypeChat];


下面加入


self.conversation = conversation;




  addDataSourcesWithMessage 方法中的


[self.dataSources addObject:msg];


下面加入


// 3.设置消息为已读取

[self.conversation markMessageWithId:msg.messageId asRead:YES];




打开   Main.storyboard


找到  XMGChatViewController 控制器


设置   Storyboard ID:ChatPage



运行





09.面试题



(1)是否使用过XMPP,XMPP的实现原理   参看PPT-2731


XMPP是一个即时通讯的协议,它规范了用于即时通信在网络上数据传输格式的,比如登录,获取好友列表等等的格式。XMPP在网络传输的数据是XML格式

比如登录:把用户名和密码放在xml的标签中,传输到服务器


XMPP是一个基于个Socket通过的网络协议,目的是为了保存长连接,以实现即时通讯功能


XMPP的客户端是使用一个XMPPFramework框架实现


XMPP的服务器是使用Openfire,一个开源的服务器


客户端获取到服务器发送过来的好友消息,客户端需要对XML进行解析,使用的解析框架的KissXML框架,而不是NSXMLParser/GDataXML




(2)在使用XMPP的时候有没有需要什么困难


发送附件(图片,语音,文档)时比较麻烦


XMPP框架没有提供发送附件的功能,需要自己实现

实现方法,把文件上传到文件服务器,上传成功后获取文件保存路径,再把附件的路径发送给好友



(3是否使用过环信,简单的说下环信的实现原理

 

 

 环信是一个即时通信的服务提供商

 环信使用的是XMPP协议,它是再XMPP的基础上进行二次开发,对服务器Openfire和客户端进行功能模型的添加和客户端SDK的封装,环信的本质还是使用XMPP,基本于Socket的网络通信

 

 环信内部实现了数据缓存,会把聊天记录添加到数据库,把附件(如音频文件,图片文件)下载到本地,使程序员更多时间是花到用户即时体验上

 

 环信内部已经实现了视频,音频,图片,其它附件发送功能

 

 环信使用公司可以节约时间成本

 不需要公司内部搭建服务器

 客户端的开发,使用环信SDK比使用XMPPFramework更简洁方便

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值