XMPPFramework iOS开发(七)即时通讯
来源:互联网 发布:知乎专栏搜索 编辑:程序博客网 时间:2024/06/10 05:09
XMPPFramework iOS开发(七)即时通讯(文字和图片)
一、程序目标
聊天界面如图,需要实现的功能有:发送文字消息、发送图片消息。
二、使用准备
2.1 开启消息模块
和电子名片、花名册模块一样,要想使用XMPP框架下的聊天功能,需要开启消息模块。
//消息模块#import "XMPPMessageArchiving.h"#import "XMPPMessageArchivingCoreDataStorage.h"
//消息模块@property (nonatomic, strong, readonly) XMPPMessageArchiving *msgArchiving;//消息数据存储模块@property (nonatomic, strong, readonly) XMPPMessageArchivingCoreDataStorage *msgArchivingStorage;
//4.添加消息模块 if (_msgArchivingStorage == nil) { _msgArchivingStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init]; _msgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_msgArchivingStorage]; [_msgArchiving activate:_xmppStream]; }
要记得在之前说过的teardown方法里面,释放花名册、消息模块等资源。此外,XMPP还提供了断线重连的功能,很简单,声明后激活即可。
//断网自动连接 XMPPReconnect *_reconnect;
//5.自动连接模块 _reconnect = [[XMPPReconnect alloc] init]; [_reconnect activate:_xmppStream];
2.2 声明成员变量
@interface WCChatViewController : UIViewController/** * 正在聊天的好友的Jid */@property (nonatomic, strong) XMPPJID *friendJid;@end
@interface WCChatViewController () <NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>{ NSFetchedResultsController *_resultContr;}@property (weak, nonatomic) IBOutlet UITableView *tableView;/** * 输入框距离底部的约束 */@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;@property (weak, nonatomic) IBOutlet UITextField *textField;- (IBAction)addBtnClick:(id)sender;@end
2.3 聊天数据格式
可以在程序中打印message的内容,其格式如下:
其中,接受到的message中:
1. from、to指通信双方的jid
2. body指通信内容
发送给对方的message中:
1. type的值是自己定义的
2. body同样指通信内容
2.4 监听键盘弹出情况
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kbWillShow:) name:UIKeyboardWillShowNotification object:nil];[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kbWillHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self];}#pragma mark 键盘即将显示- (void)kbWillShow:(NSNotification *)noti{ CGFloat kbHeight = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; self.bottomConstraint.constant = kbHeight;}#pragma mark 键盘即将隐藏- (void)kbWillHide:(NSNotification *)noti{ self.bottomConstraint.constant = 0;}#pragma mark 退出键盘- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ [self.view endEditing:YES];}
三、发送/接收消息
3.1 获取聊天数据
和花名册模块一样,XMPP会先把聊天数据上传到服务器,再缓存到本地并生成sqlite文件。因此,获取的方法也是一样的:
//加载数据库的聊天数据 //1.上下文 NSManagedObjectContext *msgContext = [[WCXMPPTool sharedWCXMPPTool].msgArchivingStorage mainThreadManagedObjectContext]; //2.请求查询哪张表 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"]; //设置过滤条件,只要自己和当前好友的聊天数据 NSString *loginUserJid = [WCXMPPTool sharedWCXMPPTool].xmppStream.myJID.bare; //数据库表的属性名可在XMPP/Extensions/XEP-0136/CoreDataStorage/XMPPMessageArchiving.xcdatamodeld/中查看 //streamBareJidStr指自己的jid,bareJidStr指和自己通信的用户的jid NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@", loginUserJid, self.friendJid.bare]; request.predicate = predicate; //如果以时间排序,timestamp是固定写法 NSSortDescriptor *timeSort = [NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:YES]; request.sortDescriptors = @[timeSort]; //3.执行请求 //3.1创建结果控制器 _resultContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:msgContext sectionNameKeyPath:nil cacheName:nil]; _resultContr.delegate = self; //3.2执行 NSError *error = nil; [_resultContr performFetch:&error]; //列表下拉到最底部 if (_resultContr.fetchedObjects.count > 0) { NSIndexPath *lastIndex = [NSIndexPath indexPathForRow:_resultContr.fetchedObjects.count-1 inSection:0]; [self.tableView scrollToRowAtIndexPath:lastIndex atScrollPosition:UITableViewScrollPositionBottom animated:YES]; }
3.2 发送文本消息
#pragma mark 按下键盘的发送键(遵守了UITextFieldDelegate协议)- (BOOL)textFieldShouldReturn:(UITextField *)textField{ NSString *text = self.textField.text; //发送聊天数据 //上面说过,chat是定义给message的xml数据中type节点的值,之后获取聊天内容就根据这个节点的值获取 XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; [msg addBody:text]; //sendElement在发送在线状态的时候也用过 [[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg]; //清空输入框 self.textField.text = nil; return YES;}
3.3 发送图片消息
#pragma mark 选择附件按钮- (IBAction)addBtnClick:(id)sender { //选择图库中的图片 UIImagePickerController *imagePc = [[UIImagePickerController alloc] init]; imagePc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; imagePc.delegate = self; //进入图库选择图片 [self presentViewController:imagePc animated:YES completion:nil];}#pragma mark 图片选择器代理方法#pragma mark 图片选择完成- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ ZHLog(@"%@", info); //获取到图片 UIImage *image = info[UIImagePickerControllerOriginalImage]; //把图片转化为data类型并发送 [self sendAttchmentWithData:UIImagePNGRepresentation(image) bodyType:@"image"]; //退出图片选择器 [self dismissViewControllerAnimated:YES completion:nil];}#pragma mark 发送附件- (void)sendAttchmentWithData:(NSData *)data bodyType:(NSString *)bodyType{ XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; //新增属性节点,存放发送的消息数据的类型 [msg addAttributeWithName:@"bodyType" stringValue:bodyType]; //发送图片其实不需要body了,但没有body就发送不出去,获取图片的时候需要另外处理body的内容,不要让它显示 [msg addBody:bodyType]; //图片的data数据经过base64编码 转换为 字符串类型 NSString *base64Str = [data base64EncodedStringWithOptions:0]; //添加自定义节点,body存放文本消息/消息类型,自定义节点存放图片的数据内容 XMPPElement *attachment = [XMPPElement elementWithName:@"attachment" stringValue:base64Str]; [msg addChild:attachment]; //发送 [[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg];}
3.4 显示消息内容
#pragma mark 返回列表行数- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return _resultContr.fetchedObjects.count;}#pragma mark 设置列表数据,显示聊天内容- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *ID = @"message"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID]; } XMPPMessageArchiving_Message_CoreDataObject *msgObj = _resultContr.fetchedObjects[indexPath.row]; //1.获取原始的xml数据 XMPPMessage *message = msgObj.message; //2.获取消息的类型 NSString *bodyType = [message attributeStringValueForName:@"bodyType"]; if ([bodyType isEqualToString:@"image"]) { //图片 //遍历message的子节点 NSArray *children = message.children; for (XMPPElement *note in children) { //获取节点的名字,attachment是刚刚定义的存放图片数据的节点 if ([[note name] isEqualToString:@"attachment"]) { //获取附件字符串,转换为data,再转为图片 NSString *imgBase64Str = [note stringValue]; NSData *imgData = [[NSData alloc] initWithBase64EncodedString:imgBase64Str options:0]; UIImage *image = [UIImage imageWithData:imgData]; cell.imageView.image = image; cell.textLabel.text = nil; } } }else { //纯文本 cell.textLabel.text = msgObj.body; cell.imageView.image = nil; } return cell;}
3.5 刷新列表数据
#pragma mark 列表内容改变- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{ //刷新列表 [self.tableView reloadData]; //列表下拉到最底部 NSIndexPath *lastIndex = [NSIndexPath indexPathForRow:_resultContr.fetchedObjects.count-1 inSection:0]; [self.tableView scrollToRowAtIndexPath:lastIndex atScrollPosition:UITableViewScrollPositionBottom animated:YES];}
四、问题
上面的代码是可以运行的,但同时还有几个地方没做好:
聊天数据的排版。上面的代码没有对聊天数据进行排版,因此可以显示,但不能知道某条消息是谁发送的,是什么时候发送的。要解决这个问题,需要自定义cell,这里不细讲,以后写UITableView相关的博客的时候再说。
发送图片耗能过大。一般来说,图片数据不应该直接发送,开发这种应用应该配备一台文件服务器。正确的发送图片的方式应该是这样的:
a) 发送图片时首先把图片上传到文件服务器
b) 服务器返回文件路径给用户
c) openfire服务器只保存这个文件路劲
d) 对方接收图片的时候,只获取到文件路径
e) 之后再根据这个路径去文件服务器下载图片
解决这个问题需要自己编写文件服务器,笔者不具备这方面的知识,所以略过不谈。
五、小结
以上。
0 0
- XMPPFramework iOS开发(七)即时通讯
- XMPPFrameWork IOS 开发(七)消息回执
- XMPPFrameWork IOS 开发(七)消息回执
- XMPPFrameWork IOS 开发(七)消息回执
- XMPPFrameWork IOS 开发(七)消息回执
- [置顶] XMPPFrameWork IOS 开发(七)消息回执
- XMPPFrameWork IOS 开发(一)xmpp简介
- XMPPFrameWork IOS 开发(二)- xcode配置
- XMPPFrameWork IOS 开发(三)登录
- XMPPFrameWork IOS 开发(六)聊天室
- XMPPFrameWork IOS 开发(一)xmpp简介
- XMPPFrameWork IOS 开发(三)登录
- XMPPFrameWork IOS 开发(六)聊天室
- XMPPFrameWork IOS 开发(一)xmpp简介
- [置顶] XMPPFrameWork IOS 开发(三)登录
- [置顶] XMPPFrameWork IOS 开发(六)聊天室
- XMPPFramework iOS开发(二)用户模型
- XMPPFramework iOS开发(四)注册登录
- 工具链编译选项
- 静态代理和动态代理(JDK动态代理和CGLib动态代理)
- 接收系统广播
- HTML学习笔记(3) - HTML标签
- C#开发:openfiledialog的使用
- XMPPFramework iOS开发(七)即时通讯
- android日常使用积累
- GIMP如何保留文字轮廓去除文字颜色
- UNIX网络编程:读写锁
- 短信验证码是什么?在网站中起到什么作用?
- 初识android内存的一些解决方案
- unity 常用的几种相机跟随
- recycleview的复用问题
- POJ - 2376 Cleaning Shifts