iOS開發之環信(三)---界面搭建

聊天控制器(ChatViewController)界面搭建

14.聊天界面-工具條排版

1)搭建界面

添加聊天控制器:到mainstoryboard中找到addressbook的tableview控制器,將cell拖線給一個uiviewcontroller選擇show,在該控制器導航欄中間拖一個navigationitem,修改名為“聊天界面”,拖一個uiew尺寸與底部tabar一樣大小,并隱藏底部的tabar(1.點擊控制器bottombar選擇為none.2.點擊控制器勾選Hide Bottom Bar

on Push)。(注意輸入框為textview),中間部分拖一個tableview,自動布局,設置代理,注意tableview中有個屬性,拖動時隱藏鍵盤(scrollview-keyboard-選擇dissmiss on drag)

自定義該聊天控制器:chatviewcontroller,將storyboard中控制器class改為該控制器的類名。

2)實現鍵盤退出、彈出,工具欄緊鏈接功能:控制器代碼如下:

//1.將工具條底部的約束拖線為該控制器的屬性。

@property(weak,nonatomic)IBOutletNSLayoutConstraint*chatInputBottomConstant;

//2.viewdidload中監聽鍵盤彈出/回方法

//1).監聽鍵盤彈出,把inputToolbar(輸入工具條)往上移

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(kbWillShow:)name:UIKeyboardWillShowNotificationobject:nil];

//2).監聽鍵盤退出,inputToolbar恢復原位

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(kbWillHide:)name:UIKeyboardWillHideNotificationobject:nil];

//3.實現監聽方法

#pragma mark鍵盤顯示時會觸發的方法

-(void)kbWillShow:(NSNotification*)noti{

//1.獲取鍵盤高度

//1.1獲取鍵盤結束時候的位置

CGRectkbEndFrm = [noti.userInfo[UIKeyboardFrameEndUserInfoKey]CGRectValue];

CGFloatkbHeight = kbEndFrm.size.height;

//2.更改inputToolbar底部約束

self.chatInputBottomConstant.constant= kbHeight;

//添加動畫

[UIViewanimateWithDuration:0.25animations:^{

[self.viewlayoutIfNeeded];

}];

}

#pragma mark鍵盤退出時會觸發的方法

-(void)kbWillHide:(NSNotification*)noti{

//inputToolbar恢復原位

self.chatInputBottomConstant.constant=0;

}

//4.注銷監聽者

-(void)dealloc{

[[NSNotificationCenterdefaultCenter]removeObserver:self];

}

15.聊天界面-接收方cell的排版

經過分析,cell有三種類型:接受方cell、發送方cell、時間cell。

在剛才拖入的tableview上拖一個cell重命名為receivecell,再改cell中拖入控件并自動布局,給cell設置重用標識為receivecell

注意:布局消息框:思想:底部為圖片,上面為label,先布局label,然后布局imageview,讓他與label大小一樣,然后修改約束使iamgeview周圍都比label周圍大一點。

A.布局label:Label布局為距離頂部15,左邊20。換行:點擊label-lines = 0、(設置label最大寬度)點擊尺子- preferred width = 242勾選,回車

B.布局uiiamgeview:cell中拖一個UIimageview,從左邊的列表中找到該imageview,拖到label的后面,同時選中label、iamgeview、設置四邊距都對齊約束,然后update,給iamgeview添加背景圖片,圖片拉伸:點擊imageview-streching-x:0.5/y:0.7,其余都為0。找到iamgeview的所有約束,給四周約束分別調10個間距大小。

自定義cell,view-ChatCell,將剛才storyboard中的cell的class改為該類。將上面那個label拖線到.h文件(必須)為屬性

控制器中數據源和代理的方法實現如下(測試而已):

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

return20;

}

-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{

return200;

}

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

staticNSString*ID =@"ReceiverCell";

ChatCell*cell = [tableViewdequeueReusableCellWithIdentifier:ID];

//顯示內容

cell.chatLabel.text=@"asrqwerqwerqwerqwerqwfaskdjlfalsk;dfjalksjdflaksjdfa;sjdfklajsdkfljasfkal;sdkfjalksjdfa;jsdflkajsdf;lajskdf;las";

returncell;

}

16.聊天界面-發送方cell的排版

1).tableview中再拖一個cell,同理搭建sendcell(注意:將label脫線到cell中的同一個屬性中)

2).設置cell的高度:在自定義cell中實現計算cell的高度)

//專門用來計算高度的一個cell(注意理解)

#import

staticNSString*ReceiverCell =@"ReceiverCell";

staticNSString*SenderCell =@"SenderCell";

@interfaceChatCell :UITableViewCell

@property(weak,nonatomic)IBOutletUILabel*chatLabel;

-(CGFloat)cellHeghit;

@end

#import"ChatCell.h"

@implementationChatCell

/**返回cell的高度*/

-(CGFloat)cellHeghit{

//1.重新布局子控件

[selflayoutIfNeeded];

return5+10+self.chatLabel.bounds.size.height+10+5;

}

@end

3).控制器中實現的方法

a.添加屬性

@property(weak,nonatomic)IBOutletUITableView*tableView;

/**數據源*/

@property(nonatomic,strong)NSMutableArray*dataSources;

/**計算高度的cell工具對象(就是一個工具,給我文字長度我就能計算高度)*/

@property(nonatomic,strong)ChatCell*chatCellTool;

b.viewdidload初始化數據源

-(NSMutableArray*)dataSources{

if(!_dataSources) {

_dataSources= [NSMutableArrayarray];

}

return_dataSources;

}

//初始化數(好幾個長的)據

[self.dataSourcesaddObject:@"xcsafasdffsadfa"];

[self.dataSourcesaddObject:@"xcsafasdfsadxcssafasdfsadfafa"];

[self.dataSourcesaddObject:@"xcsafasdfxcsaadfasadfa"];

//給計算高度的cell工具對象賦值(就是初始化工具對象,也可以為sendcell)

self.chatCellTool= [self.tableViewdequeueReusableCellWithIdentifier:ReceiverCell];

c.重新實現數據源方法

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

returnself.dataSources.count;

}

-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{

//設置label的數據

#warning計算高度與前,一定要給chatLabel.text賦值

self.chatCellTool.chatLabel.text=self.dataSources[indexPath.row];

return[self.chatCellToolcellHeghit];

}

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

ChatCell*cell =nil;

if(indexPath.row%2==0) {//發送方的cell

cell = [tableViewdequeueReusableCellWithIdentifier:SenderCell];

}else{//接收發方的cell

cell = [tableViewdequeueReusableCellWithIdentifier:ReceiverCell];

}

//顯示內容

cell.chatLabel.text=self.dataSources[indexPath.row];

returncell;

}

消息管理

消息:IM交互實體,在SDK中對應的類型是EMMessage。EMMessage由EMMessageBody組成.

總結:與消息相關的網絡請求都用到[[EMClientsharedClient].chatManager聊天管理者

獲取一個人的會話列表用EMConversation類

17.聊天界面-發送聊天消息

步驟:

在mainstoryboard中找到聊天界面的輸入框textView,點擊-return-send

監聽該send事件:拖線該textfield的代理到聊天控制器(ChatViewController)。控制器遵守代理(UITextViewDelegate),并實現代理方法

導入頭文件:#import "EMSDK.h"(用多了可以添加到pch文件中)

1).給該控制器添加一個外部屬性

//要發送人的名字

@property(nonatomic,copy)NSString*buddy;

2).在通訊錄控制器(AddressBookViewController)中傳值,代碼如下:

-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender{

//往聊天控制器傳遞一個buddy的值

iddestVC = segue.destinationViewController;

if([destVCisKindOfClass:[ChatViewControllerclass]]) {

//獲取點擊的行

NSIntegerselectedRow = [self.tableViewindexPathForSelectedRow].row;

ChatViewController*chatVc = destVC;

chatVc.buddy=self.boddyList[selectedRow];

}

}

3).聊天控制器(ChatViewController)中發送消息代碼如下

#pragma mark -UITextViewDelegate

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

//監聽Send事件--判斷最后的一個字符是不是換行字符(因為點擊send時,textfield會換行)

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

NSLog(@"發送操作");

[selfsendMessage:textView.text];

//清空textView的文字

textView.text=nil;

}

}

//發送消息

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

//把最后一個換行字符去除

#warning換行字符只占用一個長度

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

//消息=消息頭+消息體

#warning每一種消息類型對象不同的消息體

//EMTextMessageBody:文字

//EMImageMessageBody:圖片

//EMLocationMessageBody:位置

//EMVoiceMessageBody:語音

//EMVideoMessageBody:視頻

//EMFileMessageBody:文件

//EMCmdMessageBody:透傳

//1.創建文字消息體

EMTextMessageBody*body = [[EMTextMessageBodyalloc]initWithText:text];

NSString*from = [[EMClientsharedClient]currentUsername];

//2.生成Message消息對象

EMMessage*message = [[EMMessagealloc]initWithConversationID:self.buddyfrom:fromto:self.buddybody:bodyext:nil];

message.chatType=EMChatTypeChat;//設置為單聊消息

//消息類型為:

//EMChatTypeChat:設置為單聊消息

// EMChatTypeGroupChat:設置為群聊消息

//EMChatTypeChatRoom:設置為聊天室消息

//3.發送消息

//發送所有類型的消息都用這個接口,只是消息類型不同

[[EMClientsharedClient].chatManagersendMessage:messageprogress:^(intprogress) {

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

NSLog(@"完成消息發送%@",error);

}];

// 4.把消息添加到數據源,然后再刷新表格(發送完消息立即顯示)

[self.dataSourcesaddObject:message];

[self.tableViewreloadData];

// 5.把消息顯示在頂部(發送完消息自動滾動到鍵盤頂部)

[selfscrollToBottom];

}

-(void)scrollToBottom{

//1.獲取最后一行

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

return;

}

NSIndexPath*lastIndex = [NSIndexPathindexPathForRow:self.dataSources.count-1inSection:0];

[self.tableViewscrollToRowAtIndexPath:lastIndexatScrollPosition:UITableViewScrollPositionBottomanimated:YES];

}

18.聊天界面-添加本地聊天記錄

1).在聊天控制器中的viewdidload方法中添加當前navigation的title為好友的名字,加載本地聊天記錄

//顯示好友的名字

self.title=self.buddy;

//加載本地數據庫聊天記錄(MessageV1)

[selfloadLocalChatRecords];

-(void)loadLocalChatRecords{

//要獲取本地聊天記錄使用會話對象

//獲取一個會話

//EMConversationTypeChat單聊會話

//EMConversationTypeGroupChat群聊會話

//EMConversationTypeChatRoom聊天室會話

EMConversation*conversation = [[EMClientsharedClient].chatManagergetConversation:self.buddytype:EMConversationTypeChatcreateIfNotExist:YES];

//加載與當前聊天用戶所有聊天記錄

// fromUser:若傳好友名字,則只會加載好友發的聊天記錄,若為nil加載兩方的

longlongtimestamp = [[NSDatedate]timeIntervalSince1970] *1000+1;

[conversationloadMessagesWithType:EMMessageBodyTypeTexttimestamp:timestampcount:100fromUser:nilsearchDirection:EMMessageSearchDirectionUpcompletion:^(NSArray*aMessages,EMError*aError) {

//aMessages內部為EMMessage對象

//添加到數據源

[self.dataSourcesaddObjectsFromArray:aMessages];

[self.tableViewreloadData];

}];

}

2).在自定義cellChatCell中添加外部模型屬性,并實現set方法,封裝

/**消息模型,內部set方法顯示文字*/

@property(nonatomic,strong)EMMessage*message;

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

_message= message;

// 1.獲取消息體

idbody = message.body;

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

EMTextMessageBody*textBody = body;

self.chatLabel.text= textBody.text;

}elseif([bodyisKindOfClass:[EMVoiceMessageBodyclass]]){//語音消息

self.chatLabel.text=@"【語音】";

}

else{

self.chatLabel.text=@"未知類型";

}

}

3).在聊天控制器中修改tableview的數據源方法

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

returnself.dataSources.count;

}

-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{

//設置label的數據

NSLog(@"%@",self.dataSources[indexPath.row]);

// 1.獲取消息模型

EMMessage*msg =self.dataSources[indexPath.row];

self.chatCellTool.message= msg;

return[self.chatCellToolcellHeghit];

}

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

//1.先獲取消息模型

EMMessage*message =self.dataSources[indexPath.row];

ChatCell*cell =nil;

if([message.fromisEqualToString:self.buddy]) {//接收方

cell = [tableViewdequeueReusableCellWithIdentifier:ReceiverCell];

}else{//發送方

cell = [tableViewdequeueReusableCellWithIdentifier:SenderCell];

}

//顯示內容

cell.message= message;

returncell;

}

29.聊天界面-監聽消息回復(將對方發的消息及時顯示在當前聊天界面)

聊天控制器中的viewdidload中設置聊天管理器代理并遵守協議EMChatManagerDelegate

[[EMClientsharedClient].chatManageraddDelegate:selfdelegateQueue:nil];

//實現代理方法

#pragma mark -EMChatManagerDelegate

//收到一條以上消息

- (void)messagesDidReceive:(NSArray*)aMessages{

for(EMMessage*messageinaMessages) {

#warning from一定等于當前聊天用戶才可以刷新數據(當test1 - test7兩者聊天時,test8發過來的消息不能顯示在該聊天記錄界面)

if([message.fromisEqualToString:self.buddy]) {

//1.把接收的消息添加到數據源

[self.dataSourcesaddObject:message];

//2.刷新表格

[self.tableViewreloadData];

//3.顯示數據到底部

[selfscrollToBottom];

}

}

}

20.完善聊天輸入框

實現輸入框自動改變高度:

1).找到mainstoryboard中的聊天控制器界面的ChatInputToolBar找到他的高度屬性,并拖線到聊天控制器成為其屬性

@property(weak,nonatomic)IBOutletNSLayoutConstraint*inputbarHeightConstant;

2).在聊天控制器ChatViewController中的監聽文字改變的方法中添加:

#pragma mark -UITextViewDelegate

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

//監視textView.contentOffset的變化

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

// 1.計算TextView的高度,調整整個intputbar的高度

CGFloattextViewH =0;

CGFloatminHeight =33;//textView最小的高度

CGFloatmaxHeight =68;//textView最大的高度

//獲取contentSize的高度(因為textView繼承自scrollview)

CGFloatcontentHeight = textView.contentSize.height;

if(contentHeight < minHeight) {

textViewH = minHeight;

}elseif(contentHeight > maxHeight){

textViewH = maxHeight;

}else{

textViewH = contentHeight;

}

// 2.監聽Send事件--判斷最后的一個字符是不是換行字符(因為點擊send時,textfield會換行)

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

NSLog(@"發送操作");

[selfsendMessage:textView.text];

//清空textView的文字

textView.text=nil;

//發送時,textViewH的高度為33

textViewH = minHeight;

}

// 3.調整整個InputToolBar高度

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

//加個動畫

[UIViewanimateWithDuration:0.25animations:^{

[self.viewlayoutIfNeeded];

}];

// 4.記光標回到原位

#warning技巧

[textViewsetContentOffset:CGPointZeroanimated:YES];

[textViewscrollRangeToVisible:textView.selectedRange];

}

3).給textview添加背景:

在textview后面加一個背景圖片(imageview),在mainstoryboard中找到ChatInputToolBar,往里面拖一個imageview,設置其約束與textview一樣,添加背景圖片(并設置拉伸效果),將textView的背景色改為透明

21.發送語音

1).點擊左邊的話筒按鈕,輸入框變為發語音框,自動布局

在mainstoryboard中的聊天控制器界面的ChatInputToolBar中拖一個button,直接蓋住textView上面,修改文字為“按住說話”,修改高亮時的文字為“松開發送”,設置約束,固定高度,選擇默認為隱藏,拖線監聽左邊的話筒按鈕:

//語音框

@property(weak,nonatomic)IBOutletUIButton*recordBtn;

//監聽語音點擊按鈕

- (IBAction)voiceAction:(id)sender {

//1.顯示錄音按鈕

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

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

}

2).到環信官方demo中EaseUI-中找到DeviceHelper導入框架,EaseLocalDefine.h/EaseUIResource.bundle也導入框架

推進項目的Lib文件。再改控制器中導入頭文件#import"EMCDDeviceManager.h"

拖線監聽語音框,監聽按下去時,動作(選中該按鈕,),找到點下觸發,拖線監聽:

#pragma mark按鈕點下去開始錄音

//開始錄音

- (IBAction)beginVoiceAction:(id)sender {

//文件名以時間命名(根據時間定義文件的名字)

//filename:錄音將要存放的文件(自動存放沙盒)

intx =arc4random() %100000;

NSTimeIntervaltime = [[NSDatedate]timeIntervalSince1970];

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

NSLog(@"按鈕點下去開始錄音");

//這個方法,就是那個框架(DeviceHelper)中的方法

[[EMCDDeviceManagersharedInstance]asyncStartRecordingWithFileName:fileNamecompletion:^(NSError*error) {

if(!error) {

NSLog(@"開始錄音成功");

}

}];

}

#pragma mark手指從按鈕范圍內松開結束錄音

//結束錄音

- (IBAction)endVoiceAction:(id)sender {

[[EMCDDeviceManagersharedInstance]asyncStopRecordingWithCompletion:^(NSString*recordPath,NSIntegeraDuration,NSError*error) {

//recordPath:錄音的路徑

//aDuration:錄音的時長

if(!error) {

NSLog(@"錄音成功");

NSLog(@"%@",recordPath);

//發送語音給服務器

[selfsendVoice:recordPathduration:aDuration];

}else{

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

}

}];

}

#pragma mark手指從按鈕外面松開取消錄音

//取消錄音

- (IBAction)cancelVoiceAction:(id)sender {

[[EMCDDeviceManagersharedInstance]cancelCurrentRecording];

}

#pragma mark發送語音消息

//發送語音

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

//1.創建語音消息體

EMVoiceMessageBody*body = [[EMVoiceMessageBodyalloc]initWithLocalPath:recordPathdisplayName:@"語音"];

body.duration= (int)duration;

NSString*from = [[EMClientsharedClient]currentUsername];

//2.生成voice消息對象

EMMessage*voice = [[EMMessagealloc]initWithConversationID:self.buddyfrom:fromto:self.buddybody:bodyext:nil];

voice.chatType=EMChatTypeChat;//設置為單聊消息

//3.發送消息

//發送所有類型的消息都用這個接口,只是消息類型不同

[[EMClientsharedClient].chatManagersendMessage:voiceprogress:^(intprogress) {

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

NSLog(@"完成消息發送%@",error);

}];

// 4.把消息添加到數據源,然后再刷新表格(發送完消息立即顯示)

[self.dataSourcesaddObject:voice];

[self.tableViewreloadData];

// 5.把消息顯示在頂部(發送完消息自動滾動到鍵盤頂部)

[selfscrollToBottom];

}

22.播放語音

1).//顯示語音的形式為圖片加文字,在ChatCell中實現方法:

#pragma mark返回語音富文本

-(NSAttributedString*)voiceAtt{

//創建一個可變的富文本

NSMutableAttributedString*voiceAttM = [[NSMutableAttributedStringalloc]init];

// 1.接收方:富文本=圖片+時間

if([self.reuseIdentifierisEqualToString:ReceiverCell]) {

// 1.1接收方的語音圖片

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

// 1.2創建圖片附件

NSTextAttachment*imgAttachment = [[NSTextAttachmentalloc]init];

imgAttachment.image= receiverImg;

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

// 1.3圖片富文本

NSAttributedString*imgAtt = [NSAttributedStringattributedStringWithAttachment:imgAttachment];

[voiceAttMappendAttributedString:imgAtt];

// 1.4.創建時間富文本

//獲取時間

EMVoiceMessageBody*voiceBody = (EMVoiceMessageBody*)self.message.body;

NSIntegerduration = voiceBody.duration;

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

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

[voiceAttMappendAttributedString:timeAtt];

}else{

// 2.發送方:富文本=時間+圖片

// 2.1拼接時間

//獲取時間

EMVoiceMessageBody*voiceBody = (EMVoiceMessageBody*)self.message.body;

NSIntegerduration = voiceBody.duration;

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

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

[voiceAttMappendAttributedString:timeAtt];

// 2.1拼接圖片

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

//創建圖片附件

NSTextAttachment*imgAttachment = [[NSTextAttachmentalloc]init];

imgAttachment.image= receiverImg;

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

//圖片富文本

NSAttributedString*imgAtt = [NSAttributedStringattributedStringWithAttachment:imgAttachment];

[voiceAttMappendAttributedString:imgAtt];

}

return[voiceAttMcopy];

}

2).在該方法中該setMessage:(EMMessage*)message:

self.chatLabel.text = @"【語音】";

self.chatLabel.attributedText= [selfvoiceAtt];

3).給語音消息添加點擊手勢,并監聽方法:(在mainstoryboard中找到,消息label打開用戶交互)

//導入頭文件:#import"EMCDDeviceManager.h"#import"AudioPlayTool.h"

-(void)awakeFromNib{

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

// 1.給label添加敲擊手勢

UITapGestureRecognizer*tap = [[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(messageLabelTap:)];

[self.chatLabeladdGestureRecognizer:tap];

}

#pragma mark messagelabel點擊的觸發方法

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

NSLog(@"%s",__func__);

//播放語音

//只有當前的類型是為語音的時候才播放

//1.獲取消息體

idbody =self.message.body;

if([bodyisKindOfClass:[EMVoiceMessageBodyclass]]) {

NSLog(@"播放語音");

BOOLreceiver = [self.reuseIdentifierisEqualToString:ReceiverCell];

[AudioPlayToolplayWithMessage:self.messagemsgLabel:self.chatLabelreceiver:receiver];

}

}

4).寫一個工具類來播放語音goup –chart-Tool-AudioPlayTool

#import

#import"EMSDK.h"

@interfaceAudioPlayTool :NSObject

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

@end

#import"AudioPlayTool.h"

#import"EMCDDeviceManager.h"

//用imageview播放動畫(好好研究)

staticUIImageView*animatingImageView;//正在執行動畫的ImageView

@implementationAudioPlayTool

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

//把以前的動畫移除

[animatingImageViewstopAnimating];

[animatingImageViewremoveFromSuperview];

//1.播放語音

//獲取語音路徑

EMVoiceMessageBody*voiceBody = (EMVoiceMessageBody*) msg.body;

// localPath:本地音頻路徑

// remotePath:服務器音頻路徑

//本地語音文件路徑

NSString*path = voiceBody.localPath;

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

NSFileManager*manager = [NSFileManagerdefaultManager];

if(![managerfileExistsAtPath:path]) {

path = voiceBody.remotePath;

}

//播放語音

[[EMCDDeviceManagersharedInstance]asyncPlayingWithPath:pathcompletion:^(NSError*error) {

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

//移除動畫

[animatingImageViewstopAnimating];

[animatingImageViewremoveFromSuperview];

}];

//2.添加動畫(點擊語音消息時有動畫)讓UIImageView播放動畫、動畫的內部實現為圖片的輪播

//2.1創建一個UIImageView添加到Label上

UIImageView*imgView = [[UIImageViewalloc]init];

[msgLabeladdSubview:imgView];

//2.2添加動畫圖片

if(receiver) {

imgView.animationImages=@[[UIImageimageNamed:@"chat_receiver_audio_playing000"],

[UIImageimageNamed:@"chat_receiver_audio_playing001"],

[UIImageimageNamed:@"chat_receiver_audio_playing002"],

[UIImageimageNamed:@"chat_receiver_audio_playing003"]];

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

}else{

imgView.animationImages=@[[UIImageimageNamed:@"chat_sender_audio_playing_000"],

[UIImageimageNamed:@"chat_sender_audio_playing_001"],

[UIImageimageNamed:@"chat_sender_audio_playing_002"],

[UIImageimageNamed:@"chat_sender_audio_playing_003"]];

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

}

imgView.animationDuration=1;

[imgViewstartAnimating];

animatingImageView= imgView;

}

@end

5).//解決bug:當輸入多行文字時,再點語音按鈕,此時的textView欄不恢復原來高度,則:

在聊天控制器XMGChatViewController中的

//監聽語音點擊按鈕

- (IBAction)voiceAction:(id)sender {

// 1.顯示錄音按鈕

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

if(self.recordBtn.hidden==NO) {//錄音按鈕要顯示

//InputToolBar的高度要回來默認(46);

self.inputbarHeightConstant.constant=46;

//隱藏鍵盤

[self.viewendEditing:YES];

}else{

//當不錄音的時候,鍵盤顯示

[self.textViewbecomeFirstResponder];

//恢復InputToolBar高度(輸入幾行文字,點擊語音按鈕,再點會文字按鈕,textView高度不變)

[selftextViewDidChange:self.textView];

}

}

23.發送圖片顯示圖片

A.發送圖片

1).點擊輸入框的右邊的+按鈕,到相冊選擇圖片

拖線到聊天控制器中監聽方法:

- (IBAction)showImgPickerAction:(id)sender {

//顯示圖片選擇的控制器

UIImagePickerController*imgPicker = [[UIImagePickerControlleralloc]init];

//設置源

imgPicker.sourceType=UIImagePickerControllerSourceTypePhotoLibrary;

imgPicker.delegate=self;

[selfpresentViewController:imgPickeranimated:YEScompletion:NULL];

/**用戶選中圖片的回調(代理方法)*/

-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{

//1.獲取用戶選中的圖片

UIImage*selectedImg =info[UIImagePickerControllerOriginalImage];

//2.發送圖片

[selfsendImg:selectedImg];

//3.隱藏當前圖片選擇控制器

[selfdismissViewControllerAnimated:YEScompletion:NULL];

}

}

#pragma mark發送圖片

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

//1.構造圖片消息體

/*

*第一個參數:原始大小的圖片對象1000 * 1000

*第二個參數:縮略圖的圖片對象120 * 120

*/

//將image轉化為data

NSData*imageData =UIImagePNGRepresentation(selectedImg);

EMImageMessageBody*body = [[EMImageMessageBodyalloc]initWithData:imageDatathumbnailData:nil];

NSString*from = [[EMClientsharedClient]currentUsername];

//2.生成image消息對象

EMMessage*imageMessage = [[EMMessagealloc]initWithConversationID:self.buddyfrom:fromto:self.buddybody:bodyext:nil];

imageMessage.chatType=EMChatTypeChat;//設置為單聊消息

//3.發送消息

//發送所有類型的消息都用這個接口,只是消息類型不同

[[EMClientsharedClient].chatManagersendMessage:imageMessageprogress:^(intprogress) {

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

NSLog(@"完成消息發送%@",error);

}];

// 4.把消息添加到數據源,然后再刷新表格(發送完消息立即顯示)

[self.dataSourcesaddObject:imageMessage];

[self.tableViewreloadData];

// 5.把消息顯示在頂部(發送完消息自動滾動到鍵盤頂部)

[selfscrollToBottom];

}

2).抽取方法(把發送文本的方法名改為sendText,由于發文本、圖片、語音都有這一個段代碼,因此抽成一個方法)

-(void)sendMessage:(EMMessageBody*)body{

//1.生成Message消息對象

NSString*from = [[EMClientsharedClient]currentUsername];

EMMessage*message = [[EMMessagealloc]initWithConversationID:self.buddyfrom:fromto:self.buddybody:bodyext:nil];

message.chatType=EMChatTypeChat;//設置為單聊消息

//消息類型為:

//EMChatTypeChat:設置為單聊消息

// EMChatTypeGroupChat:設置為群聊消息

//EMChatTypeChatRoom:設置為聊天室消息

//2.發送消息

//發送所有類型的消息都用這個接口,只是消息類型不同

[[EMClientsharedClient].chatManagersendMessage:messageprogress:^(intprogress) {

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

NSLog(@"完成消息發送%@",error);

}];

// 3.把消息添加到數據源,然后再刷新表格(發送完消息立即顯示)

[self.dataSourcesaddObject:message];

[self.tableViewreloadData];

// 4.把消息顯示在頂部(發送完消息自動滾動到鍵盤頂部)

[selfscrollToBottom];

}

B.顯示圖片

拖入SDWebImage框架,導入#import"UIImageView+WebCache.h"

在ChatCell。M文件中-(void)setMessage:(EMMessage*)message的方法中

添加elseif([bodyisKindOfClass:[EMImageMessageBodyclass]]){//圖片消息

[selfshowImage]; }

-(void)showImage{

//獲取圖片消息體

EMImageMessageBody*imgBody = (EMImageMessageBody*)self.message.body;

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

//設置Label的尺寸足夠顯示UIImageView

NSTextAttachment*imgAttach = [[NSTextAttachmentalloc]init];

imgAttach.bounds= thumbnailFrm;

NSAttributedString*imgAtt = [NSAttributedStringattributedStringWithAttachment:imgAttach];

self.chatLabel.attributedText= imgAtt;

//1.cell里添加一個UIImageView

[self.messageLabel addSubview:self.chatImgView];

//2.設置圖片控件為縮略圖的尺寸

self.chatImgView.frame= thumbnailFrm;

//3.下載圖片

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

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

NSFileManager*manager = [NSFileManagerdefaultManager];

//如果本地圖片存在,直接從本地顯示圖片

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

if([managerfileExistsAtPath:imgBody.thumbnailLocalPath]) {

#warning本地路徑使用fileURLWithPath方法,網絡路徑使用URLWithString:

[self.chatImgViewsd_setImageWithURL:[NSURLfileURLWithPath:imgBody.thumbnailLocalPath]placeholderImage:palceImg];

}else{

//如果本地圖片不存,從網絡加載圖片

[self.chatImgViewsd_setImageWithURL:[NSURLURLWithString:imgBody.thumbnailRemotePath]placeholderImage:palceImg];

}

}

C.修改bug

1).圖片cell會重用,重用的時候應該講圖片移除;在XMGChatCell中添加

添加屬性

/**聊天圖片控件*/

@property(nonatomic,strong)UIImageView*chatImgView;

-(UIImageView*)chatImgView{

if(!_chatImgView) {

_chatImgView= [[UIImageViewalloc]init];

}

return_chatImgView;

}

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

//重用時,把聊天圖片控件移除

[self.chatImgView removeFromSuperview];

滑動聊天控制器時不能在播放語音:

在AudioPlayTool中添加方法

+(void)stop;

+(void)stop{

//停止播放語音

[[EMCDDeviceManagersharedInstance]stopPlaying];

//移除動畫

[animatingImageViewstopAnimating];

[animatingImageViewremoveFromSuperview];

}

在聊天控制器中調用:

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

//停止語音播放

[AudioPlayToolstop];

}

24.顯示時間的cell

時間顯示的規則

同一分中內的消息,只顯示一個時間

/*

15:52

msg1 15:52:10

msg2 15:52:08

msg2 15:52:02

*/

/*今天:時:分(HH:mm)

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

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

*/

在mainstoryboard中的聊天控制器中拖一個時間cell,拖一個label自動布局,自定義cell(TimeCell)為該類,并將label拖線為屬性。設置重用標識。

分析:數據源數組中不僅有模型對象,也有時間字符串對象,因此,在數據源實現cell方法中,導入TimeCell頭文件添加

//判斷數據源類型

if([self.dataSources[indexPath.row]isKindOfClass:[NSStringclass]]) {//顯示時間cell

XMGTimeCell*timeCell = [tableViewdequeueReusableCellWithIdentifier:@"TimeCell"];

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

returntimeCell;

}

在cell高度數據源方法中設置時間cell的高度

//時間cell的高度是固定

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

return18;

}

25.顯示時間的計算

新建一個時間計算工具類,繼承自nsobject(思想牛逼)

#import

@interfaceTimeTool : NSObject

//時間戳

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

@end

#import"TimeTool.h"

@implementationTimeTool

+(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 = [calendarcomponents: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 = [calendarcomponents:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDayfromDate: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];

}

@end

由于datasource數組需要判斷什么時間添加模型,什么時間添加時間字符串因此寫一個獨立的方法:在聊天控制器中修改

/**當前添加的時間*/

@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 = [XMGTimeTooltimeStr:msg.timestamp];

if(![self.currentTimeStrisEqualToString:timeStr]) {

[self.dataSourcesaddObject:timeStr];

self.currentTimeStr= timeStr;

}

// 2.再加EMMessage

[self.dataSourcesaddObject:msg];

}

修改加載數據方法:

-(void)loadLocalChatRecords{

//假設在數組的第一位置添加時間

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

//要獲取本地聊天記錄使用會話對象

EMConversation*conversation = [[EaseMobsharedInstance].chatManagerconversationForChatter:self.buddy.usernameconversationType:eConversationTypeChat];

//加載與當前聊天用戶所有聊天記錄

NSArray*messages = [conversationloadAllMessages];

//for (id obj in messages) {

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

//}

//添加到數據源

//[self.dataSources addObjectsFromArray:messages];

for(EMMessage*msgObjinmessages) {

[selfaddDataSourcesWithMessage:msgObj];

}

}

修改聊天控制器中添加數據源數組方法:

-(void)sendMessage:(id)body{

[selfaddDataSourcesWithMessage:msgObj];

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

//1.把接收的消息添加到數據源

//[self.dataSources addObject:message];

[selfaddDataSourcesWithMessage:message];

27.顯示歷史會話記錄(就是顯示給誰聊過天)

在會話控制器XMGConversationViewController中

/**歷史會話記錄*/

@property(nonatomic,strong)NSArray*conversations;

viewDidLoad添加:

//添加聊天管理者代理

[[EMClientsharedClient].chatManageraddDelegate:selfdelegateQueue:nil];

//獲取歷史會話記錄

[selfloadConversations];

-(void)loadConversations{

//獲取歷史會話記錄

//1.從內存中獲取歷史回話記錄,獲取內存中所有會話

//獲取所有會話,如果內存中不存在會從DB中加載

NSArray*conversations = [[EMClientsharedClient].chatManagergetAllConversations];

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

self.conversations= conversations;

//顯示總的未讀數

[selfshowTabBarBadge];

}

-(void)showTabBarBadge{

//遍歷所有的會話記錄,將未讀取的消息數進行累

NSIntegertotalUnreadCount =0;

for(EMConversation*conversationinself.conversations) {

totalUnreadCount += [conversationunreadMessagesCount];

}

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

}

在mainstoryboard中找到會話控制器,設置cell的重用標識,cell的style為subtitle

在會話控制器中實現數據源方法

#pragma mark - Table view data source

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

returnself.conversations.count;

}

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

staticNSString*ID =@"ConversationCell";

UITableViewCell*cell = [tableViewdequeueReusableCellWithIdentifier:ID];

//獲取會話模型

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

//顯示數據

// 1.顯示用戶名

cell.textLabel.text= [NSStringstringWithFormat:@"%@ ====未讀消息數:%zd",conversaion.conversationId,conversaion.unreadMessagesCount];

// 2.顯示最新的一條記錄

//獲取消息體

idbody = conversaion.latestMessage.body;

if([bodyisKindOfClass:[EMTextMessageBodyclass]]) {

EMTextMessageBody*textBody = body;

cell.detailTextLabel.text= textBody.text;

}elseif([bodyisKindOfClass:[EMVoiceMessageBodyclass]]){

EMVoiceMessageBody*voiceBody = body;

cell.detailTextLabel.text= [voiceBodydisplayName];

}elseif([bodyisKindOfClass:[EMImageMessageBodyclass]]){

EMImageMessageBody*imgBody = body;

cell.detailTextLabel.text= imgBody.displayName;

}else{

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

}

returncell;

}

實現一些代理方法:

#pragma mark - EMChatManagerDelegate

//會話列表發生變化調用

- (void)conversationListDidUpdate:(NSArray*)aConversationList{

//給數據源重新賦值

self.conversations= aConversationList;

//刷新表格

[self.tableViewreloadData];

//顯示總的未讀數

[selfshowTabBarBadge];

}

//收到消息

//一旦接受到消息,刷新未讀消息列表

-(void)messagesDidReceive:(NSArray*)aMessages{

//更新表格

[self.tableViewreloadData];

//顯示總的未讀數

[selfshowTabBarBadge];

}

27.設置消息為已讀

點擊會話界面的cell,會跳到聊天控制器

導入聊天控制器頭文件

在mainstoryboard中聊天控制器中設置storyboardIDChatPage

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

//進入到聊天控制器

//1.從storybaord加載聊天控制器

ChatViewController*chatVc = [[UIStoryboardstoryboardWithName:@"Main"bundle:nil]instantiateViewControllerWithIdentifier:@"ChatPage"];

//會話

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

//2.設置好友屬性

chatVc.buddy= conversation.conversationId;

//3.展現聊天界面

[self.navigationControllerpushViewController:chatVcanimated:YES];

}

到聊天控制器中設置消息為已讀

添加一個屬性

/**當前會話對象*/

@property(nonatomic,strong)EMConversation*conversation;

-(void)loadLocalChatRecords{

self.conversation= conversation;

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

// 3.設置消息為已讀取

//將消息設置為已讀

//aMessageId要設置消息的ID

//pError錯誤信息

[self.conversationmarkMessageAsReadWithId:msg.messageIderror:nil];

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 概述在iOS開發中UITableView可以說是使用最廣泛的控件,我們平時使用的軟件中到處都可以看到它的影子,類似...
    liudhkk閱讀 9,088評論 3 38
  • 1.badgeVaule氣泡提示 2.git終端命令方法> pwd查看全部 >cd>ls >之后桌面找到文件夾內容...
    i得深刻方得S閱讀 4,752評論 1 9
  • 上官網注冊賬號 首先來到環信的官網,然后登陸.沒有賬號先注冊一個. 進去之后創建應用,如圖 創建應用界面 點擊確定...
    loneWolf01閱讀 525評論 0 0
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,210評論 30 471
  • 看路上 那些 大大小小 緩慢爬行的“海爬狗” 看江上 那些 層層疊疊 穿梭往來的“浪里白” 看街上 那些 花花綠綠...
    聞丁閱讀 542評論 3 1