之前有做过和即时通讯相关的项目,不过貌似已经下架了,源码也找不到了。因为下个项目可能会涉及即时通讯,所以最近几天把环信即使通讯的相关东西又稍微在整理了一些,写了个小的demo。demo中有登录、注册、发送文字、图片、声音消息、群聊、单聊、获取会话列表、未读消息通知、自定义聊天界面、自定义键盘、增加删除好好和组等功能,总的来说功能还算齐全,写的也比较随意,整个结构没给整理好,因为最开始想的就是随便熟悉一下,谁知后来写了很多功能。这个demo中的聊天界面以及自定义键盘都是自己写的,目前还存在一些不完善的地方,以后有时间了回给补充一下,如发送消息时聊天界面进度信息。简单看一下效果图。
其中的聊天面板和键盘都是自己写的,键盘只是写了个大概,还没有增加自定义表情,后期会继续完善,可能会增加图文混排功能,自定义表情功能。例外就是因为项目结构没有搭建的很好,所以最开始在处理收到好友加自己为好友这样的功能存在一些问题。这里主要是告诉一些新手使用环信实现这种即时通讯app的一些注意事项,以及搭建聊天界面和自定义键盘比较好的思路。实际上搭建一个功能比较完善的聊天界面还是很麻烦的,一般情况下很多人可能就会直接选择EaseUI。当然,如果想挑战一下,不防稍微试试。
1.关闭控制台登录信息的打印。
EMError *error = [[EaseMob sharedInstance]registerSDKWithAppKey:@"zhengyawei#zhuanxintestzyw" apnsCertName:@"HuanXinp12" otherConfig:@{kSDKConfigEnableConsoleLogger:@NO}];
2.调用环信的程序加载完毕的方法,才能添加代理。之后才能调用代理方法,否则代理方法不生效。
[[EaseMob sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
3.发送文本消息是要注意,去掉\n字符,否则可能聊天界面会无缘无故多出一行。记得之前在这个上面吃过亏,当时怎么也找不出原因。
//**1.接收信息者
/*==============================*/
NSString *receiver = self.isGroup? self.group.groupId: self.budddy.username;
//**2.内容对象
//这里去掉\n
EMChatText *chatText = [[EMChatText alloc]initWithText:[textView.text substringToIndex:textView.text.length - 1]];
//**3.创建文本消息体
EMTextMessageBody *textBody = [[EMTextMessageBody alloc]initWithChatObject:chatText];
//**4.创建EMMessage对象
EMMessage *msg = [[EMMessage alloc]initWithReceiver:receiver bodies:@[textBody]];
/*==============================*/
msg.messageType = self.isGroup? eMessageTypeGroupChat:eMessageTypeChat;
//**5.异步发送消息
[[EaseMob sharedInstance].chatManager asyncSendMessage:msg progress:self prepare:^(EMMessage *message, EMError *error) {
NSLog(@"准备发送文本消息");
} onQueue:nil completion:^(EMMessage *message, EMError *error) {
NSLog(@"文本消息发送成功");
[self.messageData addObject:message];
[self scrollBottom];
//发送成功后清除数据
textView.text = @"";
} onQueue:nil];
};
4.添加过代理后,必须设置移除代理,所有界面都是如此
-(void)dealloc{
[[EaseMob sharedInstance].chatManager removeDelegate:self];
}
- 下面这个方法主要是聊天管理器, 获取该对象后, 可以做登录、聊天、加好友等操作。注意,在获取会话列表之前要调用这句代码,否则可能出现会话列表部分信息显示不完全。
[[EaseMob sharedInstance].chatManager loadDataFromDatabase];
6.- (void)didReceiveBuddyRequest:(NSString *)username message:(NSString *)message;类似这种收到好友请求和被被人拉入群的通知,这样的代理方法要放到tabBarController中记性管理。如果随便放置,可能收不到这样的通知,而且控制台会打印错误信息。
7.关于聊天界面的布局。
在聊天界面布局的时候最好是在控制器的view上,再添加一个sccrollView,然后再将聊天界面的tableView以及自定义键盘的工具条放置到这个scrolleView上。这样更能方便在键盘弹出时,管理tableView上消息位置的滑动。思路是这样的:在键盘弹出的时候,让整个scrollView都忘上升。这样工具条和tableView都会同步上升。另外还要充分利用tableView的contentInset属性,自我感觉这个属性如果用好了,在实际项目中可以处理很多问题。在聊天界面中,这个属性的主要解决的问题是:防止聊天记录中内容过少,键盘弹出时聊天记录看不见。只需简单一行代码就能解决这个问题。代码如下:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrameNotification:) name:UIKeyboardWillChangeFrameNotification object:nil];
- (void)keyboardWillChangeFrameNotification:(NSNotification *)noti
{
CGRect keyboardF = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
if (keyboardF.origin.y < kWeChatScreenHeight) {
self.contentView.top = - keyboardF.size.height;
//设置tableView的内边距,防止聊天记录中内容过少,键盘弹出时聊天记录看不见
self.chatTableView.contentInset = UIEdgeInsetsMake(keyboardF.size.height, 0, 0, 0);
//键盘弹出时,tableView也要滚到最底部
//这里要做这样的判断,防止没有聊天记录的时候,出现数组越界
if (self.messageData.count != 0 ) {
[self.chatTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messageData.count - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}else{
self.contentView.top = 0;
self.chatTableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
}
8.聊天界面的文字消息、图片消息以及声音消息,建议都使用UIButton这个空间进行处理。因为这些声音消息和图片消息,都涉及到点击事件。可能后期如果想扩充地理位置,视频消息也会涉及点击事件,如果都是添加点击手势,感觉会过于麻烦。因为UIBUtton本身就带有image和titleLabel属性,所以图片和文字消息都可以在上面进行展示。调节UIButton上的图片和文字位置主要可以通过下面这两行代码:
//具体位置根据实际需求进行调节
self.chatBtn.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
self.chatBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
9.关于自定义键盘如果更方便的在键盘和自定义视图之间进行切换。这里主要提供两种思路。
第一种方法是,将聊天面板添加到[UIApplication sharedApplication].keyWindow上,在切换键盘的时候控制聊天面板的y值。不过这种方法使用起来设计到的逻辑判断会比较多,不是很方便,更推荐使用第二种方法。
第二种发放实现思路,充分利用UITextView或UITextField的inputView属性,首先要知道当_textView.inputView = nil的时候,则显示输入视图inputView就是系统的键盘;如果是自己_textView.inputView = customeView,这是显示的就是自己自定义的视图。通过在nil和customeView这两个值之间的切换,就可以实现在键盘和自定义视图之间的切换,根本没有涉及太多的逻辑。注意:如果想封装一个属于自己的自定义键盘,这个inputView属性起到的作用很大。
#pragma mark - 表情和键盘面板切换
- (void)keyBoardButtonClick:(UIButton *)btn{
if (btn.isSelected == NO) {//切换到表情
_textView.inputView = self.faceBoard;
btn.selected = YES;
}else{//切换到系统键盘
UIView *view = [[UIView alloc]init];
view.backgroundColor = [UIColor cyanColor];
view.frame = CGRectMake(0, 0, self.view.frame.size.width, 100);
_textView.inputView = nil;
btn.selected = NO;
}
if (_textView.isFirstResponder == YES) {
[_textView resignFirstResponder];
[_textView becomeFirstResponder];
}else{
[_textView becomeFirstResponder];
}
}
Demo下载地址:https://pan.baidu.com/s/1nuF1puL