xmpp发送图片信息和声音信息,xmpp图片信息

图片和音频文件发送的基本思路就是:

先将图片转化成二进制文件,然后将二进制文件进行base64编码,编码后成字符串。在即将发送的message内添加一个子节点,节点的stringValue(节点的值)设置这个编码后的字符串。然后消息发出后取出消息文件的时候,通过messageType 先判断是不是图片信息,如果是图片信息先通过自己之前设置的节点名称,把这个子节点的stringValue取出来,应该是一个base64之后的字符串,

往期回顾:

xmpp整理笔记:聊天信息的发送与显示  http://www.cnblogs.com/dsxniubility/p/4307073.html

xmpp整理笔记:环境的快速配置(附安装包)  http://www.cnblogs.com/dsxniubility/p/4304570.html

xmpp整理笔记:xmppFramework框架的导入和介绍  http://www.cnblogs.com/dsxniubility/p/4307057.html

xmpp整理笔记:用户网络连接及好友管理 http://www.cnblogs.com/dsxniubility/p/4307066.html

一。图片发送

如果你不是在皮皮瑞的简书看到本文,请点击查看原文

图片是通过界面的加号点击弹出相册界面,然后点击相册中的某张图片,相册退下,图片发出

[objc]view plaincopy

- (IBAction)setPhoto {

UIImagePickerController*picker = [[UIImagePickerControlleralloc]init];

picker.delegate=self;

[selfpresentViewController:pickeranimated:YEScompletion:nil];

}

这是加号点击方法,之后设置UIImagePickerController的代理,然后再遵守对应的协议

这里需要注意的是,遵守了UIImagePickerControllerDelegate的 同时还必须要遵守 UINavigationControllerDelegate。协议

下面就是弹出相册点击了一张图片后触发的代理方法,都是常用方法在此也不过多解释。

[objc]view plaincopy

#pragma mark - ******************** imgPickerController代理方法

- (void)imagePickerController:(UIImagePickerController*)pickerdidFinishPickingMediaWithInfo:(NSDictionary*)info

{

UIImage*image = info[UIImagePickerControllerOriginalImage];

NSData*data = UIImagePNGRepresentation(image);

[selfsendMessageWithData:databodyName:@"image"];

[selfdismissViewControllerAnimated:YEScompletion:nil];

}

其中的sendMessageWithData: bodyName: 是自定义的方法

此方法的功能就是传入一个data二进制文件 和 文件的类型,就把这个文件发出去。

之所有在后面有bodyName,让用户传入一个类型名,是为了区分发送图片和发送音频

方法内代码如下:

[objc]view plaincopy

/** 发送二进制文件 */

- (void)sendMessageWithData:(NSData*)databodyName:(NSString*)name

{

XMPPMessage*message = [XMPPMessagemessageWithType:@"chat"to:self.chatJID];

[messageaddBody:name];

// 转换成base64的编码

NSString*base64str = [database64EncodedStringWithOptions:0];

// 设置节点内容

XMPPElement*attachment = [XMPPElementelementWithName:@"attachment"stringValue:base64str];

// 包含子节点

[messageaddChild:attachment];

// 发送消息

[[SXXMPPToolssharedXMPPTools].xmppStreamsendElement:message];

}

这个方法内流程就是一开始说得,先编码再发送。这个自定义的方法同样适用于发送音频信息。

二。图片的显示

这个是在tableView数据源方法中,取出信息即将赋值之前多了一层判断,如果是图片信息,采用下面的方法赋值。

关于基本发送流程哪里忘了可以查看普通文本信息的发送方法:

[objc]view plaincopy

if([message.bodyisEqualToString:@"image"]) {

XMPPMessage*msg = message.message;

for(XMPPElement*node in msg.children) {

// 取出消息的解码

NSString*base64str = node.stringValue;

NSData*data = [[NSDataalloc]initWithBase64EncodedString:base64stroptions:0];

UIImage*image = [[UIImagealloc]initWithData:data];

// 把图片在label中显示

NSTextAttachment*attach = [[NSTextAttachmentalloc]init];

attach.image= [imagescaleImageWithWidth:200];

NSAttributedString*attachStr = [NSAttributedStringattributedStringWithAttachment:attach];

// 用了这个label的属性赋值方法,就可以忽略那个普通的赋值方法

cell.messageLabel.attributedText= attachStr;

[self.viewendEditing:YES];

}

}

这其中用到了一个 scaleImageWithWidth:方法,这个方法是传入一个允许的最大宽度width,然后这个方法内部先判断,如片大小是否超过最大值,如果没有超过最大值就是图片有多大发多大,如果图片的尺寸超过了最大宽度,就把图片的整体尺寸都等比例缩小到正好等于最大宽度的尺寸。这其中要用到Quartz2D的上下文的知识。

这个方法可以写成UIimage的分类,代码如下

[objc]view plaincopy

/** 把图片缩小到指定的宽度范围内为止 */

- (UIImage*)scaleImageWithWidth:(CGFloat)width{

if(self.size.width

returnself;

}

CGFloat scale =self.size.width/width;

CGFloat height =self.size.height/scale;

CGRect rect = CGRectMake(0,0, width, height);

// 开始上下文 目标大小是 这么大

UIGraphicsBeginImageContext(rect.size);

// 在指定区域内绘制图像

[selfdrawInRect:rect];

// 从上下文中获得绘制结果

UIImage*resultImage = UIGraphicsGetImageFromCurrentImageContext();

// 关闭上下文返回结果

UIGraphicsEndImageContext();

returnresultImage;

}

三。音频的发送

音频的发送,与之前图片的发送,有一定的相似,也有一些不同。音频发送的核心思想,是按下按钮开始录音,松开手结束录音并且保存录音。因此需要处理按钮的按下和抬手两个监听方法。但是其中有一个苹果的bug: 自定义的按钮无法同时处理TouchUpInSide 和 TouchDown。 就是按下按钮不松手是一个打印,手一松开一个打印。这是不行的,都是手一松两个同时打印。(除非按钮特别大,一般小按钮无法同时监听这两个点击事件)。但是苹果自带的系统按钮却可以,不管多小,比如buttonWithTypeAdd(小加号按钮)都可以,因此设置点击声音按钮之后下面出现一个inputView,上面是可以同时处理这两个时间的按钮。通过这个按钮来控制开始录音和结束录音。保存之后,也是转化成data二进制文件,然后再通过base64编码。然后加入子节点,和图片类似发过去。接收的时候,也是取出节点内的stringValue解码。但是显示在tableview的cell中的是声音的时间,点击这个cell触发声音播放时间。从而播放音频。播放时cell内部的某些样式变化也是可以控制的。

先把界面中的声音按钮的点击事件连线。

[objc]view plaincopy

- (IBAction)setRecord {

// 切换焦点,弹出录音按钮

[self.recordTextbecomeFirstResponder];

}

其实就是自己随便写了个textField 点击时就让他获取焦点,然后下面弹出一个输入框上面有按钮

这个textField的懒加载如下

[objc]view plaincopy

- (UITextField*)recordText {

if(_recordText ==nil) {

_recordText = [[UITextFieldalloc]init];

UIButton*btn = [UIButtonbuttonWithType:UIButtonTypeContactAdd];

_recordText.inputView= btn;

[btnaddTarget:selfaction:@selector(startRecord)forControlEvents:UIControlEventTouchDown];

[btnaddTarget:selfaction:@selector(stopRecord)forControlEvents:UIControlEventTouchUpInside];

[self.inputMessageViewaddSubview:_recordText];

}

return_recordText;

}

对于音频文件的一系列处理操作,最好抽出一个工具类写好,然后在需要的时候直接调用,并且以后其他项目也可以拖过去直接使用。

首先需要用到的属性如下。

[objc]view plaincopy

@interfaceSXRecordTools ()

/** 录音器 */

@property(nonatomic,strong)AVAudioRecorder*recorder;

/** 录音地址 */

@property(nonatomic,strong)NSURL*recordURL;

/** 播放器 */

@property(nonatomic,strong)AVAudioPlayer*player;

/** 播放完成时回调 */

@property(nonatomic,copy)void(^palyCompletion)();

@end

至于其中的开始录音和结束录音方法如下

[objc]view plaincopy

/** 开始录音 */

- (void)startRecord{

[self.recorderrecord];

}

/** 停止录音 */

- (void)stopRecordSuccess:(void(^)(NSURL*url,NSTimeInterval time))successandFailed:(void(^)())failed

{

// 只有在这里才能取到currentTime

NSTimeInterval time =self.recorder.currentTime;

[self.recorderstop];

if(time <1.5) {

if(failed) {

failed();

}

}else{

if(success) {

success(self.recordURL,time);

}

}

}

开始录音和结束录音,框架中都自己有方法。主要是判断了一下,音频的时长,小于1.5秒会回调录音失败的代码块。

这里需要注意的是, recorder.currentTime 当前录音的时长,只有在这个方法中才能取到,出了方法就取不到值了。

然后在控制器中,那个小加号按钮的按下和抬起的监听方法中调用工具类中的方法

[objc]view plaincopy

#pragma mark - ******************** 录音方法

- (void)startRecord {

NSLog(@"开始录音");

[[SXRecordToolssharedRecorder]startRecord];

}

- (void)stopRecord {

NSLog(@"停止录音");

[[SXRecordToolssharedRecorder]stopRecordSuccess:^(NSURL*url, NSTimeInterval time) {

// 发送声音数据

NSData*data = [NSDatadataWithContentsOfURL:url];

[selfsendMessageWithData:databodyName:[NSStringstringWithFormat:@"audio:%.1f秒",time]];

}andFailed:^{

[[[UIAlertViewalloc]initWithTitle:@"提示"message:@"时间太短"delegate:nilcancelButtonTitle:@"确定"otherButtonTitles:nil,nilnil]show];

}];

}

可以清楚的看到,发送声音调sendMessageWithData:时把声音的时长当做参数bodyName 传入。 然后就会将这个字符串存到message的子节点内发出。

四。音频文件的显示

也是和图片一样,对于本行取出的信息先判断是不是音频信息,如果是,遍历节点,取出字符串,并且截取了一下,截取掉“audio:”,让tableView的cell中只显示 时长,

[objc]view plaincopy

elseif([message.bodyhasPrefix:@"audio"]){

XMPPMessage*msg = message.message;

for(XMPPElement*node in msg.children) {

NSString*base64str = node.stringValue;

NSData*data = [[NSDataalloc]initWithBase64EncodedString:base64stroptions:0];

NSString*newstr = [message.bodysubstringFromIndex:6];

cell.messageLabel.text= newstr;

cell.audioData= data;

}

}

这个audioData是个专门用来存放声音文件的信息。但是表格是可以重用的,为了让一个刚刚重用的cell里面的音频文件别形成冲突,叠加。建议在刚取出cell时就加上一行

cell.audioData = nil;

五。关于声音文件的播放

虽然,框架自己就有声音文件的播放方法,但是还需要做很多附加操作,建议先在工具类中写一个方法,就是播放data文件,并且设置完成后的回调代码。即playData:completion: 。在播放的方法中先判断声音是否正在播放,如果正在播放则不做任何操作。然后在方法中设置player的代理,这样可以通过代理方法来监听声音文件何时播放完,触发代理方法。因此这个传入的completion代码块必须要先用成员变量记录下,然后在声音文件播放完的代理方法中再执行此代码块

[objc]view plaincopy

- (void)playData:(NSData*)datacompletion:(void(^)())completion

{

// 判断是否正在播放

if(self.player.isPlaying) {

[self.playerstop];

}

// 记录块代码

self.palyCompletion= completion;

// 监听播放器播放状态

self.player= [[AVAudioPlayeralloc]initWithData:dataerror:NULL];

self.player.delegate=self;

[self.playerplay];

}

代理方法在声音文件播放完的代理方法中再执行保存的代码块

[objc]view plaincopy

#pragma mark - ******************** 完成播放时的代理方法

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)playersuccessfully:(BOOL)flag

{

if(self.palyCompletion) {

self.palyCompletion();

}

}

工具类中的方法写完了之后,可以去外面调用了。给自己这个自定义的SXChatCell添加一个点击方法。默认情况下按钮是默认颜色的,点击时颜色变成红色,然后播放完成时的回调代码再把颜色恢复成默认颜色。

[objc]view plaincopy

- (void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event {

// 如果有音频数据,直接播放音频

if(self.audioData!=nil) {

// 播放音频

self.messageLabel.textColor= [UIColorredColor];

// 如果单例的块代码中包含self,一定使用weakSelf

__weakSXChatCell*weakSelf =self;

[[SXRecordToolssharedRecorder]playData:self.audioDatacompletion:^{

weakSelf.messageLabel.textColor= [UIColorblackColor];

}];

}

}

如图红色的那个cell是正在播放

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容