即时通讯

即时通讯

6
回复

【环信官方外包一个项目】利用环信IM实现一个在线白板 环信

ppdd8951 回复了问题 • 7 人关注 • 1441 次浏览 • 2017-11-15 14:42 • 来自相关话题

1
评论

【开源项目】一个基于环信IM开发的开源的私密社交APP-Baby 即时通信 Baby

seven。 发表了文章 • 1473 次浏览 • 2017-08-31 14:49 • 来自相关话题

Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。先上效果图




开屏页的登录和注册




编辑个人信息




相册页面




首页的Moment
版本更新:
 
version 1.6
加入Tinker 热修复更新部分依赖修复大量细节问题,加入部分注释

version 1.5
bug fixupdate sth

version 1.4
增加长按删除功能优化Rxbus订阅加载数据外国友人优化的一些细节等等

version 1.3
增加了评论功能优化了相册加载修复了一些内存泄漏等等

version 1.2
修复了一些Bug把登陆注册事件换了个Zip操作符更符合流的思想

version 1.1
修复了主页背景无法切换的问题修复了聊天推送的问题修了语音视频的问题做了一些细节修改
Download



安装包下载体验
 
github地址
github源码
 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。Thanks for improving my code m-ezzat.

Contacts
Email:379489343zhi@gmail.comQQ:379489343
 Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 查看全部
   Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。
先上效果图

003.png

开屏页的登录和注册


001.png

编辑个人信息


002.png

相册页面


004.png

首页的Moment


版本更新:
 
version 1.6
  1. 加入Tinker 热修复
  2. 更新部分依赖
  3. 修复大量细节问题,加入部分注释


version 1.5
  1. bug fix
  2. update sth


version 1.4
  1. 增加长按删除功能
  2. 优化Rxbus订阅加载数据
  3. 外国友人优化的一些细节
  4. 等等


version 1.3
  1. 增加了评论功能
  2. 优化了相册加载
  3. 修复了一些内存泄漏
  4. 等等


version 1.2
  1. 修复了一些Bug
  2. 把登陆注册事件换了个Zip操作符更符合流的思想


version 1.1
  1. 修复了主页背景无法切换的问题
  2. 修复了聊天推送的问题
  3. 修了语音视频的问题
  4. 做了一些细节修改

Download

 
github地址

 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
  • 感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。
  • Thanks for improving my code m-ezzat.


Contacts

 
Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
6
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 7371 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
0
评论

【环信征文】集成环信,并实现消息免打扰 即时通讯 环信 消息免打扰 IM iOS

cokeyer 发表了文章 • 139 次浏览 • 2017-09-14 11:27 • 来自相关话题

现在大多数社交app都有消息免打扰功能,因为环信SDK主要是对即时通讯模块进行封装,因此如果要实现消息免打扰功能则需要开发者自己对此功能逻辑进行处理。
先解释一下,本人项目中对消息免打扰功能的定义:正常情况下,我们收到的每一条聊天消息都会收到小红点、声音、震动的提示。如果对某个好友设置消息免打扰功能,则只提示小红点,声音和震动则不再提示。

下面简述结合环信SDK时,此功能的实现方法。
环信将即时聊天的所有功能分为四大模块进行管理://聊天模块:
[EMClient sharedClient].chatManager
//好友模块 :
[EMClient sharedClient].contactManager
//群组模块 :
[EMClient sharedClient].groupManager
//聊天室模块:
[EMClient sharedClient].roomManager消息免打扰的功能借助聊天模块[EMClient sharedClient].chatManager的API就能实现。
一般来说,在app内不管当前在哪个界面,只要收到消息都需要被判断是否需要免打扰,因此可以在appdelegate里写如下代码,app通常都有tabBarController,那么也可以让tabBarController来实现如下代码。
首先让控制器tabBarController遵守代理EMChatManagerDelegate
然后成为代理
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

EMChatManagerDelegate中有一个代理方法- (void)didReceiveMessages:(NSArray *)aMessages;实现此代理方法- (void)didReceiveMessages:(NSArray *)aMessages{
[self setupUnreadMessageCount];
EMMessage *message = aMessages[0];
NSString *sendPerson = message.from;
BHNavigatiomController *imNaviCV = self.viewControllers[1];
BHConversationListController *converCV = imNaviCV.viewControllers[0];
[converCV refreshDataSource];//只要收到消息就从服务器拿
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {
if ([hx_name isEqualToString:sendPerson]) {
return;
}
}
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
}方法中有一个aMessages参数。这个参数是一个消息组,每一个元素都是EMMessage类型的实例。
因为通常都只有一条信息。因此只需要取出EMMessage *message = aMessages[0];
EMMessage类中有许多属性,因此根据一条信息基本可以获取想知道的所有信息。这里我们只需要知道此消息的发送方即可,也就是发送方的环信idNSString *sendPerson = message.from;下面代码中有一AppEngine.IMDataCent.data_ExcuseFriendsData,这个数组里面装的都是已经被设置消息免打扰的好友的环信id,是请求自己服务器获得的,获取的代码最好在一打开app时,就及时获取到。然后根据对好友免打扰设置的操作,访问后台接口进行增删,并刷新数组与后端保持一致即可。
通过[hx_name isEqualToString:sendPerson],遍历免打扰数组与当前消息的发送方环信id,就可以知道是否需要免打扰了。
 
下面附上完整代码//
// BHTabBarController.m
// ShangHeYiYang
//
// Created by LiBohan on 2017/8/24.
// Copyright © 2017年 xxxxx. All rights reserved.
//


//两次提示的默认间隔
static const CGFloat kDefaultPlaySoundInterval = 3.0;
static NSString *kMessageType = @"MessageType";
static NSString *kConversationChatter = @"ConversationChatter";
static NSString *kGroupName = @"GroupName";


#import "BHTabBarController.h"
#import <UserNotifications/UserNotifications.h>
#import "BHConversationListController.h"

@interface BHTabBarController ()<EMChatManagerDelegate,EMContactManagerDelegate>

@property (strong, nonatomic) NSDate *lastPlaySoundDate;

@end

@implementation BHTabBarController

- (void)viewDidLoad {
[super viewDidLoad];

[DemoCallManager sharedManager].mainController = self;

[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

[[EMClient sharedClient].contactManager addDelegate:self delegateQueue:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupUnreadMessageCount) name:@"setupUnreadMessageCount" object:nil];

}

-(void)friendshipDidRemoveByUser:(NSString *)aUsername{

// __weak __typeof(&*self)weakSelf = self;

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:contactReloadData object:nil];
});


// if ([AppEngine.mainDataCent.data_UserData.data_HxID isEqualToString:aUsername]) {


//删除好友成功后,再删除聊天会话
[[EMClient sharedClient].chatManager deleteConversation:aUsername isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError) {

if (!aError) {
//删除聊天会话,成功后,刷新聊天会话列表
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:talkBtnClickThenUpdateConversionlist object:nil];
});


}

}];
}

// 统计未读消息数
-(void)setupUnreadMessageCount
{
NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for (EMConversation *conversation in conversations) {
unreadCount += conversation.unreadMessagesCount;
}

NSArray *tabBarItems = self.tabBar.items;

UITabBarItem *conlistTabBarItem = [tabBarItems objectAtIndex:1];
if (unreadCount > 0) {
conlistTabBarItem.badgeValue = [NSString stringWithFormat:@"%i",(int)unreadCount];
}else{
conlistTabBarItem.badgeValue = nil;
}


// UIApplication *application = [UIApplication sharedApplication];
// [application setApplicationIconBadgeNumber:unreadCount];
}


- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages {
for (EMMessage *message in aCmdMessages) {
EMCmdMessageBody *body = (EMCmdMessageBody *)message.body;
NSLog(@"收到的action是 -- %@",body.action);


if (body.action == nil) {

return;

}

NSData *jsonData = [body.action dataUsingEncoding:NSUTF8StringEncoding];

NSError *err;

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

if(err) {

NSLog(@"json解析失败:%@",err);

return;

}

NSNumber *num = dic[@"count"];

NSString *str = [num stringValue];

[AppEngine.IMDataCent requestUpdateUnreadNumberWithUnreadNumber:str];

}


}

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

// [self refresh];

[self setupUnreadMessageCount];

EMMessage *message = aMessages[0];

NSString *sendPerson = message.from;

BHNavigatiomController *imNaviCV = self.viewControllers[1];

BHConversationListController *converCV = imNaviCV.viewControllers[0];

// [converCV refresh];

[converCV refreshDataSource];//只要收到消息就从服务器拿

UIApplicationState state = [[UIApplication sharedApplication] applicationState];

for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {

if ([hx_name isEqualToString:sendPerson]) {
return;
}

}

switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}

}

- (void)playSoundAndVibration{
NSTimeInterval timeInterval = [[NSDate date]
timeIntervalSinceDate:self.lastPlaySoundDate];
if (timeInterval < kDefaultPlaySoundInterval) {
//如果距离上次响铃和震动时间太短, 则跳过响铃
NSLog(@"skip ringing & vibration %@, %@", [NSDate date], self.lastPlaySoundDate);
return;
}

//保存最后一次响铃时间
self.lastPlaySoundDate = [NSDate date];

// 收到消息时,播放音频
[[EMCDDeviceManager sharedInstance] playNewMessageSound];
// 收到消息时,震动
[[EMCDDeviceManager sharedInstance] playVibration];
}


- (void)showNotificationWithMessage:(EMMessage *)message
{
EMPushOptions *options = [[EMClient sharedClient] pushOptions];
NSString *alertBody = nil;
if (options.displayStyle == EMPushDisplayStyleMessageSummary) {
EMMessageBody *messageBody = message.body;
NSString *messageStr = nil;
switch (messageBody.type) {
case EMMessageBodyTypeText:
{
messageStr = ((EMTextMessageBody *)messageBody).text;
}
break;
case EMMessageBodyTypeImage:
{
messageStr = NSLocalizedString(@"message.image", @"Image");
}
break;
case EMMessageBodyTypeLocation:
{
messageStr = NSLocalizedString(@"message.location", @"Location");
}
break;
case EMMessageBodyTypeVoice:
{
messageStr = NSLocalizedString(@"message.voice", @"Voice");
}
break;
case EMMessageBodyTypeVideo:{
messageStr = NSLocalizedString(@"message.video", @"Video");
}
break;
default:
break;
}

do {
// NSString *title = [[UserProfileManager sharedInstance] getNickNameWithUsername:message.from];
NSString *title = @"大佬";

if (message.chatType == EMChatTypeGroupChat) {
NSDictionary *ext = message.ext;
if (ext && ext[kGroupMessageAtList]) {
id target = ext[kGroupMessageAtList];
if ([target isKindOfClass:[NSString class]]) {
if ([kGroupMessageAtAll compare:target options:NSCaseInsensitiveSearch] == NSOrderedSame) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
else if ([target isKindOfClass:[NSArray class]]) {
NSArray *atTargets = (NSArray*)target;
if ([atTargets containsObject:[EMClient sharedClient].currentUsername]) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
}
NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
for (EMGroup *group in groupArray) {
if ([group.groupId isEqualToString:message.conversationId]) {
title = [NSString stringWithFormat:@"%@(%@)", message.from, group.subject];
break;
}
}
}
else if (message.chatType == EMChatTypeChatRoom)
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
NSMutableDictionary *chatrooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
NSString *chatroomName = [chatrooms objectForKey:message.conversationId];
if (chatroomName)
{
title = [NSString stringWithFormat:@"%@(%@)", message.from, chatroomName];
}
}

alertBody = [NSString stringWithFormat:@"%@:%@", title, messageStr];
} while (0);
}
else{
// alertBody = NSLocalizedString(@"receiveMessage", @"you have a new message");
alertBody = @"您有一条消息";
}

NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.lastPlaySoundDate];
BOOL playSound = NO;
if (!self.lastPlaySoundDate || timeInterval >= kDefaultPlaySoundInterval) {
self.lastPlaySoundDate = [NSDate date];
playSound = YES;
}

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:message.chatType] forKey:kMessageType];
[userInfo setObject:message.conversationId forKey:kConversationChatter];

//发送本地推送
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
if (playSound) {
content.sound = [UNNotificationSound defaultSound];
}
content.body =alertBody;
content.userInfo = userInfo;
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.messageId content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate date]; //触发通知的时间
notification.alertBody = alertBody;
notification.alertAction = NSLocalizedString(@"open", @"Open");
notification.timeZone = [NSTimeZone defaultTimeZone];
if (playSound) {
notification.soundName = UILocalNotificationDefaultSoundName;
}
notification.userInfo = userInfo;

//发送通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}

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

NSLog(@"sf");
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


 
 
本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂 查看全部
现在大多数社交app都有消息免打扰功能,因为环信SDK主要是对即时通讯模块进行封装,因此如果要实现消息免打扰功能则需要开发者自己对此功能逻辑进行处理。
先解释一下,本人项目中对消息免打扰功能的定义:正常情况下,我们收到的每一条聊天消息都会收到小红点、声音、震动的提示。如果对某个好友设置消息免打扰功能,则只提示小红点,声音和震动则不再提示。

下面简述结合环信SDK时,此功能的实现方法。
环信将即时聊天的所有功能分为四大模块进行管理:
//聊天模块:
[EMClient sharedClient].chatManager
//好友模块 :
[EMClient sharedClient].contactManager
//群组模块 :
[EMClient sharedClient].groupManager
//聊天室模块:
[EMClient sharedClient].roomManager
消息免打扰的功能借助聊天模块[EMClient sharedClient].chatManager的API就能实现。
一般来说,在app内不管当前在哪个界面,只要收到消息都需要被判断是否需要免打扰,因此可以在appdelegate里写如下代码,app通常都有tabBarController,那么也可以让tabBarController来实现如下代码。
首先让控制器tabBarController遵守代理EMChatManagerDelegate
然后成为代理
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

EMChatManagerDelegate中有一个代理方法
- (void)didReceiveMessages:(NSArray *)aMessages;
实现此代理方法
- (void)didReceiveMessages:(NSArray *)aMessages{
[self setupUnreadMessageCount];
EMMessage *message = aMessages[0];
NSString *sendPerson = message.from;
BHNavigatiomController *imNaviCV = self.viewControllers[1];
BHConversationListController *converCV = imNaviCV.viewControllers[0];
[converCV refreshDataSource];//只要收到消息就从服务器拿
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {
if ([hx_name isEqualToString:sendPerson]) {
return;
}
}
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
}
方法中有一个aMessages参数。这个参数是一个消息组,每一个元素都是EMMessage类型的实例。
因为通常都只有一条信息。因此只需要取出EMMessage *message = aMessages[0];
EMMessage类中有许多属性,因此根据一条信息基本可以获取想知道的所有信息。这里我们只需要知道此消息的发送方即可,也就是发送方的环信idNSString *sendPerson = message.from;下面代码中有一AppEngine.IMDataCent.data_ExcuseFriendsData,这个数组里面装的都是已经被设置消息免打扰的好友的环信id,是请求自己服务器获得的,获取的代码最好在一打开app时,就及时获取到。然后根据对好友免打扰设置的操作,访问后台接口进行增删,并刷新数组与后端保持一致即可。
通过[hx_name isEqualToString:sendPerson],遍历免打扰数组与当前消息的发送方环信id,就可以知道是否需要免打扰了。
 
下面附上完整代码
//
// BHTabBarController.m
// ShangHeYiYang
//
// Created by LiBohan on 2017/8/24.
// Copyright © 2017年 xxxxx. All rights reserved.
//


//两次提示的默认间隔
static const CGFloat kDefaultPlaySoundInterval = 3.0;
static NSString *kMessageType = @"MessageType";
static NSString *kConversationChatter = @"ConversationChatter";
static NSString *kGroupName = @"GroupName";


#import "BHTabBarController.h"
#import <UserNotifications/UserNotifications.h>
#import "BHConversationListController.h"

@interface BHTabBarController ()<EMChatManagerDelegate,EMContactManagerDelegate>

@property (strong, nonatomic) NSDate *lastPlaySoundDate;

@end

@implementation BHTabBarController

- (void)viewDidLoad {
[super viewDidLoad];

[DemoCallManager sharedManager].mainController = self;

[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

[[EMClient sharedClient].contactManager addDelegate:self delegateQueue:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupUnreadMessageCount) name:@"setupUnreadMessageCount" object:nil];

}

-(void)friendshipDidRemoveByUser:(NSString *)aUsername{

// __weak __typeof(&*self)weakSelf = self;

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:contactReloadData object:nil];
});


// if ([AppEngine.mainDataCent.data_UserData.data_HxID isEqualToString:aUsername]) {


//删除好友成功后,再删除聊天会话
[[EMClient sharedClient].chatManager deleteConversation:aUsername isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError) {

if (!aError) {
//删除聊天会话,成功后,刷新聊天会话列表
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:talkBtnClickThenUpdateConversionlist object:nil];
});


}

}];
}

// 统计未读消息数
-(void)setupUnreadMessageCount
{
NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for (EMConversation *conversation in conversations) {
unreadCount += conversation.unreadMessagesCount;
}

NSArray *tabBarItems = self.tabBar.items;

UITabBarItem *conlistTabBarItem = [tabBarItems objectAtIndex:1];
if (unreadCount > 0) {
conlistTabBarItem.badgeValue = [NSString stringWithFormat:@"%i",(int)unreadCount];
}else{
conlistTabBarItem.badgeValue = nil;
}


// UIApplication *application = [UIApplication sharedApplication];
// [application setApplicationIconBadgeNumber:unreadCount];
}


- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages {
for (EMMessage *message in aCmdMessages) {
EMCmdMessageBody *body = (EMCmdMessageBody *)message.body;
NSLog(@"收到的action是 -- %@",body.action);


if (body.action == nil) {

return;

}

NSData *jsonData = [body.action dataUsingEncoding:NSUTF8StringEncoding];

NSError *err;

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

if(err) {

NSLog(@"json解析失败:%@",err);

return;

}

NSNumber *num = dic[@"count"];

NSString *str = [num stringValue];

[AppEngine.IMDataCent requestUpdateUnreadNumberWithUnreadNumber:str];

}


}

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

// [self refresh];

[self setupUnreadMessageCount];

EMMessage *message = aMessages[0];

NSString *sendPerson = message.from;

BHNavigatiomController *imNaviCV = self.viewControllers[1];

BHConversationListController *converCV = imNaviCV.viewControllers[0];

// [converCV refresh];

[converCV refreshDataSource];//只要收到消息就从服务器拿

UIApplicationState state = [[UIApplication sharedApplication] applicationState];

for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {

if ([hx_name isEqualToString:sendPerson]) {
return;
}

}

switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}

}

- (void)playSoundAndVibration{
NSTimeInterval timeInterval = [[NSDate date]
timeIntervalSinceDate:self.lastPlaySoundDate];
if (timeInterval < kDefaultPlaySoundInterval) {
//如果距离上次响铃和震动时间太短, 则跳过响铃
NSLog(@"skip ringing & vibration %@, %@", [NSDate date], self.lastPlaySoundDate);
return;
}

//保存最后一次响铃时间
self.lastPlaySoundDate = [NSDate date];

// 收到消息时,播放音频
[[EMCDDeviceManager sharedInstance] playNewMessageSound];
// 收到消息时,震动
[[EMCDDeviceManager sharedInstance] playVibration];
}


- (void)showNotificationWithMessage:(EMMessage *)message
{
EMPushOptions *options = [[EMClient sharedClient] pushOptions];
NSString *alertBody = nil;
if (options.displayStyle == EMPushDisplayStyleMessageSummary) {
EMMessageBody *messageBody = message.body;
NSString *messageStr = nil;
switch (messageBody.type) {
case EMMessageBodyTypeText:
{
messageStr = ((EMTextMessageBody *)messageBody).text;
}
break;
case EMMessageBodyTypeImage:
{
messageStr = NSLocalizedString(@"message.image", @"Image");
}
break;
case EMMessageBodyTypeLocation:
{
messageStr = NSLocalizedString(@"message.location", @"Location");
}
break;
case EMMessageBodyTypeVoice:
{
messageStr = NSLocalizedString(@"message.voice", @"Voice");
}
break;
case EMMessageBodyTypeVideo:{
messageStr = NSLocalizedString(@"message.video", @"Video");
}
break;
default:
break;
}

do {
// NSString *title = [[UserProfileManager sharedInstance] getNickNameWithUsername:message.from];
NSString *title = @"大佬";

if (message.chatType == EMChatTypeGroupChat) {
NSDictionary *ext = message.ext;
if (ext && ext[kGroupMessageAtList]) {
id target = ext[kGroupMessageAtList];
if ([target isKindOfClass:[NSString class]]) {
if ([kGroupMessageAtAll compare:target options:NSCaseInsensitiveSearch] == NSOrderedSame) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
else if ([target isKindOfClass:[NSArray class]]) {
NSArray *atTargets = (NSArray*)target;
if ([atTargets containsObject:[EMClient sharedClient].currentUsername]) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
}
NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
for (EMGroup *group in groupArray) {
if ([group.groupId isEqualToString:message.conversationId]) {
title = [NSString stringWithFormat:@"%@(%@)", message.from, group.subject];
break;
}
}
}
else if (message.chatType == EMChatTypeChatRoom)
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
NSMutableDictionary *chatrooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
NSString *chatroomName = [chatrooms objectForKey:message.conversationId];
if (chatroomName)
{
title = [NSString stringWithFormat:@"%@(%@)", message.from, chatroomName];
}
}

alertBody = [NSString stringWithFormat:@"%@:%@", title, messageStr];
} while (0);
}
else{
// alertBody = NSLocalizedString(@"receiveMessage", @"you have a new message");
alertBody = @"您有一条消息";
}

NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.lastPlaySoundDate];
BOOL playSound = NO;
if (!self.lastPlaySoundDate || timeInterval >= kDefaultPlaySoundInterval) {
self.lastPlaySoundDate = [NSDate date];
playSound = YES;
}

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:message.chatType] forKey:kMessageType];
[userInfo setObject:message.conversationId forKey:kConversationChatter];

//发送本地推送
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
if (playSound) {
content.sound = [UNNotificationSound defaultSound];
}
content.body =alertBody;
content.userInfo = userInfo;
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.messageId content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate date]; //触发通知的时间
notification.alertBody = alertBody;
notification.alertAction = NSLocalizedString(@"open", @"Open");
notification.timeZone = [NSTimeZone defaultTimeZone];
if (playSound) {
notification.soundName = UILocalNotificationDefaultSoundName;
}
notification.userInfo = userInfo;

//发送通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}

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

NSLog(@"sf");
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


 
 
本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂

qrcode_for_gh_bc92a063b4a2_430.jpg
0
评论

【环信征文】基于环信开发一个医疗APP-Ⅰ iOS 环信 即时通讯 IM

cokeyer 发表了文章 • 163 次浏览 • 2017-09-12 17:56 • 来自相关话题

公司又有了新项目,依然是含有即时通讯功能模块的项目。在经历了上个项目对环信sdk的集成后,对环信EaseUI有了大概的了解。这次果断还是集成环信,一回生二回熟,最主要的还是对环信IM稳定性非常放心!项目医疗类的项目,角色分医生和患者,双方都可主动发起会话,但如果是患者找医生聊天,必须先经过预约,并只能在预约时间区间内才能和医生发送聊天消息、图片、语音、实时音视频。项目基于环信最V3.3.4版本开发,首先在会话界面自定义一个类比如BHChatViewController,继承自EaseMessageViewController类,基本上一个简单的界面就有了。以下就是EaseMessageViewController类的发送各种消息的方法,那么根据需要只需重写以下方法即可。/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@result
*/
- (void)sendTextMessage:(NSString *)text;

/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@param ext 扩展信息
@result
*/
- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext;

/*!
@method
@brief 发送图片消息
@discussion
@param image 发送图片
@result
*/
- (void)sendImageMessage:(UIImage *)image;

/*!
@method
@brief 发送位置消息
@discussion
@param latitude 经度
@param longitude 纬度
@param address 地址
@result
*/
- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address;

/*!
@method
@brief 发送语音消息
@discussion
@param localPath 语音本地地址
@param duration 时长
@result
*/
- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration;

/*!
@method
@brief 发送视频消息
@discussion
@param url 视频url
@result
*/
- (void)sendVideoMessageWithURL:(NSURL *)url;我在对发送图片的操作进行处理的时候,发现当拍照发图时,走这个方法//当你拍照发图时,走这个方法
- (void)sendImageMessage:(UIImage *)image;但当从相册选择图片发送时,会发现,不走上面的方法了。
仔细检查代码发现走了EaseMessageViewController.m的如下方法





然而这个方法,环信并没有放在EaseMessageViewController.h成为公开方法。我们只需手动粘贴方法到.h,然后在自己的子类重写就可以了。

还有一个关于更多下图更多功能区域的问题





如上图所示,相册、拍照、视频等附加功能按钮,环信用EaseChatBarMoreView类来管理的。
如果需要增加功能按钮用这个方法/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;移除某个功能按钮用这个方法/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;修改一个功能按钮用这个方法/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;可当你添加按钮,或者修改按钮时,会发现按钮的名字设置不了
然后检查环信内部的实现,发现title的值在方法里根本就没用到?!
索性不用updateItemWithImage这个方法了,直接去改内部代码。
修改代码如下

到EaseChatBarMoreView.m修改- (void)setupSubviewsForType:(EMChatToolbarType)type方法。改动的部分在代码后面有标注。- (void)setupSubviewsForType:(EMChatToolbarType)type
{
//self.backgroundColor = [UIColor clearColor];
self.accessibilityIdentifier = @"more_view";

_scrollview = [[UIScrollView alloc] init];
_scrollview.pagingEnabled = YES;
_scrollview.showsHorizontalScrollIndicator = NO;
_scrollview.showsVerticalScrollIndicator = NO;
_scrollview.delegate = self;
[self addSubview:_scrollview];

_pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
_pageControl.numberOfPages = 1;
[self addSubview:_pageControl];

CGFloat insets = (self.frame.size.width - 4 * CHAT_BUTTON_SIZE) / 5;

_photoButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_photoButton setTitle:@"相册" forState:UIControlStateNormal];//改动
[_photoButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_photoButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_photoButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_photoButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_photoButton.accessibilityIdentifier = @"image";
[_photoButton setFrame:CGRectMake(insets, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photo"] forState:UIControlStateNormal];
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photoSelected"] forState:UIControlStateHighlighted];
[_photoButton addTarget:self action:@selector(photoAction) forControlEvents:UIControlEventTouchUpInside];
_photoButton.tag = MOREVIEW_BUTTON_TAG;
[_scrollview addSubview:_photoButton];

_locationButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_locationButton setTitle:@"位置" forState:UIControlStateNormal];//改动
[_locationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_locationButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_locationButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_locationButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_locationButton.accessibilityIdentifier = @"location";
[_locationButton setFrame:CGRectMake(insets * 2 + CHAT_BUTTON_SIZE, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_location"] forState:UIControlStateNormal];
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_locationSelected"] forState:UIControlStateHighlighted];
[_locationButton addTarget:self action:@selector(locationAction) forControlEvents:UIControlEventTouchUpInside];
_locationButton.tag = MOREVIEW_BUTTON_TAG + 1;
[_scrollview addSubview:_locationButton];

_takePicButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_takePicButton setTitle:@"拍照" forState:UIControlStateNormal];//改动
[_takePicButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_takePicButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_takePicButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_takePicButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_takePicButton setFrame:CGRectMake(insets * 3 + CHAT_BUTTON_SIZE * 2, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_camera"] forState:UIControlStateNormal];
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_cameraSelected"] forState:UIControlStateHighlighted];
[_takePicButton addTarget:self action:@selector(takePicAction) forControlEvents:UIControlEventTouchUpInside];
_takePicButton.tag = MOREVIEW_BUTTON_TAG + 2;
_maxIndex = 2;
[_scrollview addSubview:_takePicButton];

CGRect frame = self.frame;
if (type == EMChatToolbarTypeChat) {
frame.size.height = 150;
_audioCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_audioCallButton setTitle:@"语音" forState:UIControlStateNormal];//改动
[_audioCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_audioCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_audioCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_audioCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_audioCallButton setFrame:CGRectMake(insets * 4 + CHAT_BUTTON_SIZE * 3, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCall"] forState:UIControlStateNormal];
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCallSelected"] forState:UIControlStateHighlighted];
[_audioCallButton addTarget:self action:@selector(takeAudioCallAction) forControlEvents:UIControlEventTouchUpInside];
_audioCallButton.tag = MOREVIEW_BUTTON_TAG + 3;
[_scrollview addSubview:_audioCallButton];

_videoCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_videoCallButton setTitle:@"视频" forState:UIControlStateNormal];//改动
[_videoCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_videoCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_videoCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_videoCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_videoCallButton setFrame:CGRectMake(insets, 10 * 2 + CHAT_BUTTON_SIZE + 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCall"] forState:UIControlStateNormal];
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCallSelected"] forState:UIControlStateHighlighted];
[_videoCallButton addTarget:self action:@selector(takeVideoCallAction) forControlEvents:UIControlEventTouchUpInside];
_videoCallButton.tag =MOREVIEW_BUTTON_TAG + 4;
_maxIndex = 4;
[_scrollview addSubview:_videoCallButton];
}
else if (type == EMChatToolbarTypeGroup)
{
frame.size.height = 80;
}
self.frame = frame;
_scrollview.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_pageControl.frame = CGRectMake(0, CGRectGetHeight(frame) - 20, CGRectGetWidth(frame), 20);
_pageControl.hidden = _pageControl.numberOfPages<=1;
}




本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂





 
项目完整源码 查看全部
公司又有了新项目,依然是含有即时通讯功能模块的项目。在经历了上个项目对环信sdk的集成后,对环信EaseUI有了大概的了解。这次果断还是集成环信,一回生二回熟,最主要的还是对环信IM稳定性非常放心!
项目医疗类的项目,角色分医生和患者,双方都可主动发起会话,但如果是患者找医生聊天,必须先经过预约,并只能在预约时间区间内才能和医生发送聊天消息、图片、语音、实时音视频。项目基于环信最V3.3.4版本开发,首先在会话界面自定义一个类比如BHChatViewController,继承自EaseMessageViewController类,基本上一个简单的界面就有了。以下就是EaseMessageViewController类的发送各种消息的方法,那么根据需要只需重写以下方法即可。
/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@result
*/
- (void)sendTextMessage:(NSString *)text;

/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@param ext 扩展信息
@result
*/
- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext;

/*!
@method
@brief 发送图片消息
@discussion
@param image 发送图片
@result
*/
- (void)sendImageMessage:(UIImage *)image;

/*!
@method
@brief 发送位置消息
@discussion
@param latitude 经度
@param longitude 纬度
@param address 地址
@result
*/
- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address;

/*!
@method
@brief 发送语音消息
@discussion
@param localPath 语音本地地址
@param duration 时长
@result
*/
- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration;

/*!
@method
@brief 发送视频消息
@discussion
@param url 视频url
@result
*/
- (void)sendVideoMessageWithURL:(NSURL *)url;
我在对发送图片的操作进行处理的时候,发现当拍照发图时,走这个方法
//当你拍照发图时,走这个方法
- (void)sendImageMessage:(UIImage *)image;
但当从相册选择图片发送时,会发现,不走上面的方法了。
仔细检查代码发现走了EaseMessageViewController.m的如下方法

环信2.png

然而这个方法,环信并没有放在EaseMessageViewController.h成为公开方法。我们只需手动粘贴方法到.h,然后在自己的子类重写就可以了。

还有一个关于更多下图更多功能区域的问题

EaseChatBarMoreView.png

如上图所示,相册、拍照、视频等附加功能按钮,环信用EaseChatBarMoreView类来管理的。
如果需要增加功能按钮用这个方法
/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;
移除某个功能按钮用这个方法
/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;
修改一个功能按钮用这个方法
/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;
可当你添加按钮,或者修改按钮时,会发现按钮的名字设置不了
然后检查环信内部的实现,发现title的值在方法里根本就没用到?!
索性不用updateItemWithImage这个方法了,直接去改内部代码。
修改代码如下

到EaseChatBarMoreView.m修改- (void)setupSubviewsForType:(EMChatToolbarType)type方法。改动的部分在代码后面有标注。
- (void)setupSubviewsForType:(EMChatToolbarType)type
{
//self.backgroundColor = [UIColor clearColor];
self.accessibilityIdentifier = @"more_view";

_scrollview = [[UIScrollView alloc] init];
_scrollview.pagingEnabled = YES;
_scrollview.showsHorizontalScrollIndicator = NO;
_scrollview.showsVerticalScrollIndicator = NO;
_scrollview.delegate = self;
[self addSubview:_scrollview];

_pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
_pageControl.numberOfPages = 1;
[self addSubview:_pageControl];

CGFloat insets = (self.frame.size.width - 4 * CHAT_BUTTON_SIZE) / 5;

_photoButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_photoButton setTitle:@"相册" forState:UIControlStateNormal];//改动
[_photoButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_photoButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_photoButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_photoButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_photoButton.accessibilityIdentifier = @"image";
[_photoButton setFrame:CGRectMake(insets, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photo"] forState:UIControlStateNormal];
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photoSelected"] forState:UIControlStateHighlighted];
[_photoButton addTarget:self action:@selector(photoAction) forControlEvents:UIControlEventTouchUpInside];
_photoButton.tag = MOREVIEW_BUTTON_TAG;
[_scrollview addSubview:_photoButton];

_locationButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_locationButton setTitle:@"位置" forState:UIControlStateNormal];//改动
[_locationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_locationButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_locationButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_locationButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_locationButton.accessibilityIdentifier = @"location";
[_locationButton setFrame:CGRectMake(insets * 2 + CHAT_BUTTON_SIZE, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_location"] forState:UIControlStateNormal];
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_locationSelected"] forState:UIControlStateHighlighted];
[_locationButton addTarget:self action:@selector(locationAction) forControlEvents:UIControlEventTouchUpInside];
_locationButton.tag = MOREVIEW_BUTTON_TAG + 1;
[_scrollview addSubview:_locationButton];

_takePicButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_takePicButton setTitle:@"拍照" forState:UIControlStateNormal];//改动
[_takePicButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_takePicButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_takePicButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_takePicButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_takePicButton setFrame:CGRectMake(insets * 3 + CHAT_BUTTON_SIZE * 2, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_camera"] forState:UIControlStateNormal];
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_cameraSelected"] forState:UIControlStateHighlighted];
[_takePicButton addTarget:self action:@selector(takePicAction) forControlEvents:UIControlEventTouchUpInside];
_takePicButton.tag = MOREVIEW_BUTTON_TAG + 2;
_maxIndex = 2;
[_scrollview addSubview:_takePicButton];

CGRect frame = self.frame;
if (type == EMChatToolbarTypeChat) {
frame.size.height = 150;
_audioCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_audioCallButton setTitle:@"语音" forState:UIControlStateNormal];//改动
[_audioCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_audioCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_audioCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_audioCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_audioCallButton setFrame:CGRectMake(insets * 4 + CHAT_BUTTON_SIZE * 3, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCall"] forState:UIControlStateNormal];
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCallSelected"] forState:UIControlStateHighlighted];
[_audioCallButton addTarget:self action:@selector(takeAudioCallAction) forControlEvents:UIControlEventTouchUpInside];
_audioCallButton.tag = MOREVIEW_BUTTON_TAG + 3;
[_scrollview addSubview:_audioCallButton];

_videoCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_videoCallButton setTitle:@"视频" forState:UIControlStateNormal];//改动
[_videoCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_videoCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_videoCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_videoCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_videoCallButton setFrame:CGRectMake(insets, 10 * 2 + CHAT_BUTTON_SIZE + 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCall"] forState:UIControlStateNormal];
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCallSelected"] forState:UIControlStateHighlighted];
[_videoCallButton addTarget:self action:@selector(takeVideoCallAction) forControlEvents:UIControlEventTouchUpInside];
_videoCallButton.tag =MOREVIEW_BUTTON_TAG + 4;
_maxIndex = 4;
[_scrollview addSubview:_videoCallButton];
}
else if (type == EMChatToolbarTypeGroup)
{
frame.size.height = 80;
}
self.frame = frame;
_scrollview.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_pageControl.frame = CGRectMake(0, CGRectGetHeight(frame) - 20, CGRectGetWidth(frame), 20);
_pageControl.hidden = _pageControl.numberOfPages<=1;
}




本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂

qrcode_for_gh_bc92a063b4a2_430.jpg

 
项目完整源码
0
评论

【环信征文】|两处小改动,解决环信V3.0官方版本关于转发的bug 转发 即时通讯 环信 IM

cokeyer 发表了文章 • 202 次浏览 • 2017-08-02 00:57 • 来自相关话题

    (本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢)
    
    最近接手了一个集成即时通讯功能的项目,用的是环信的SDK。用环信的接口可以快速实现即时通讯的很多功能。并且对官方demo稍加改动基本能够满足项目需求。真机测试时,发现图片的转发,每次都是转发失败。我开始以为是我集成时有疏漏,逐行检查代码。发现并不是我的问题。从app store下载的官方demo同样是转发失败!!坑我啊!!原因是ContactListSelectViewController这个控制器里无法正确获取到想转发的图片的缓存地址。
修改如下图:





ContactListSelectViewController.m
代码拷走直接用- (BOOL)messageViewController:(EaseMessageViewController *)viewController
didLongPressRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [self.dataArray objectAtIndex:indexPath.row];
if (![object isKindOfClass:[NSString class]]) {
EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
//////////////////////////解决转发问题的代码///////////////////////////////
EMImageMessageBody *imageBody = (EMImageMessageBody*)[cell.model.message body];
EMMessageBodyType ty = cell.model.bodyType;
if (ty == EMMessageBodyTypeImage) {
NSString *str = cell.model.message == nil ? cell.model.thumbnailFileURLPath : [imageBody localPath];
[[NSUserDefaults standardUserDefaults] setValue:str forKey:@"imgTosand"];
}
/////////////////////////解决转发问题的代码////////////////////////
[cell becomeFirstResponder];
self.menuIndexPath = indexPath;
[self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
}
return YES;
}ContactListSelectViewController是取数据,那么存数据要在ChatViewController控制器做存数据的操作。消息类型写死为EMChatTypeChat,是因为,不论是从单聊界面转发,还是从群聊界面转发,都只能转发给个人,所以这里写死,目前没有问题。
如下图:





ChatViewController.m
代码拷走直接用#pragma mark - EMUserListViewControllerDelegate
- (void)userListViewController:(EaseUsersListViewController *)userListViewController
didSelectUserModel:(id<IUserModel>)userModel
{
if (!self.messageModel) {
return;
}
if (self.messageModel.bodyType == EMMessageBodyTypeText) {
EMMessage *message = [EaseSDKHelper sendTextMessage:self.messageModel.text to:userModel.buddy messageType:EMChatTypeChat messageExt:self.messageModel.message.ext];
__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
// NSMutableArray *array = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
UIViewController *chatController = nil;
#ifdef REDPACKET_AVALABLE
chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
chatController = [[ChatViewController alloc]
initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? [userModel.nickname copy] : [userModel.buddy copy];
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakself.navigationController setViewControllers:array animated:YES];
[weakself.navigationController popViewControllerAnimated:YES];
} else {
[self showHudInView:self.view hint:Localized(@"transpondFail")];
}
}];
} else if (self.messageModel.bodyType == EMMessageBodyTypeImage) {
[self showHudInView:self.view hint:Localized(@"transponding")];
__weak typeof(self) weakSelf = self;
NSString *localPath = [(EMImageMessageBody *)self.messageModel.message.body thumbnailLocalPath];
//////////////////////////解决转发问题的代码////////////////////////////
localPath = [[NSUserDefaults standardUserDefaults] valueForKey:@"imgTosand"];
//////////////////////////解决转发问题的代码//////////////////////////
UIImage *image = [UIImage imageWithContentsOfFile:localPath];
void (^block)() = ^(EMMessage *message){
EMImageMessageBody *imgBody = (EMImageMessageBody *)message.body;
NSString *from = [[EMClient sharedClient] currentUsername];
EMImageMessageBody *newBody = [[EMImageMessageBody alloc] initWithData:nil thumbnailData:[NSData dataWithContentsOfFile:imgBody.thumbnailLocalPath]];
newBody.thumbnailLocalPath = imgBody.thumbnailLocalPath;
newBody.thumbnailRemotePath = imgBody.thumbnailRemotePath;
newBody.remotePath = imgBody.remotePath;
EMMessage *newMsg = [[EMMessage alloc] initWithConversationID:userModel.buddy from:from to:userModel.buddy body:newBody ext:message.ext];
// newMsg.chatType = message.chatType;//此为环信代码
newMsg.chatType = EMChatTypeChat;//这里是我加的
[[EMClient sharedClient].chatManager sendMessage:newMsg progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}
[(EMImageMessageBody *)message.body setLocalPath:imgBody.localPath];
[[EMClient sharedClient].chatManager updateMessage:message completion:nil];

// NSMutableArray *array = [NSMutableArray arrayWithArray:[weakSelf.navigationController viewControllers]];

#ifdef REDPACKET_AVALABLE
RedPacketChatViewController *chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
ChatViewController *chatController = [[ChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? userModel.nickname : userModel.buddy;
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakSelf.navigationController setViewControllers:array animated:YES];
[weakSelf.navigationController popViewControllerAnimated:YES];//转发完跳回去
}];
};

if (!image) {
[[EMClient sharedClient].chatManager downloadMessageThumbnail:self.messageModel.message progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}

block(message);
}];
} else {
block(self.messageModel.message);
}
}
}上面一定要判断一下消息体类型,只有消息体为图片类型(EMMessageBodyTypeImage)才需要保存图片本地。如果不做判断的话,点击气泡马上崩掉。
个人感觉虽然能解决图片转发的问题,但并不是最好的解决办法,虽然对环信demo的代码改动最少。有更好的办法,欢迎在评论区交流。
(本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢) 查看全部
    (本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢)
    
    最近接手了一个集成即时通讯功能的项目,用的是环信的SDK。用环信的接口可以快速实现即时通讯的很多功能。并且对官方demo稍加改动基本能够满足项目需求。真机测试时,发现图片的转发,每次都是转发失败。我开始以为是我集成时有疏漏,逐行检查代码。发现并不是我的问题。从app store下载的官方demo同样是转发失败!!坑我啊!!原因是ContactListSelectViewController这个控制器里无法正确获取到想转发的图片的缓存地址。
修改如下图:

2893691-7bfd41d8c42b5f3d.png

ContactListSelectViewController.m
代码拷走直接用
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
didLongPressRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [self.dataArray objectAtIndex:indexPath.row];
if (![object isKindOfClass:[NSString class]]) {
EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
//////////////////////////解决转发问题的代码///////////////////////////////
EMImageMessageBody *imageBody = (EMImageMessageBody*)[cell.model.message body];
EMMessageBodyType ty = cell.model.bodyType;
if (ty == EMMessageBodyTypeImage) {
NSString *str = cell.model.message == nil ? cell.model.thumbnailFileURLPath : [imageBody localPath];
[[NSUserDefaults standardUserDefaults] setValue:str forKey:@"imgTosand"];
}
/////////////////////////解决转发问题的代码////////////////////////
[cell becomeFirstResponder];
self.menuIndexPath = indexPath;
[self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
}
return YES;
}
ContactListSelectViewController是取数据,那么存数据要在ChatViewController控制器做存数据的操作。消息类型写死为EMChatTypeChat,是因为,不论是从单聊界面转发,还是从群聊界面转发,都只能转发给个人,所以这里写死,目前没有问题。
如下图:

2893691-7beb949a62e8035f.png

ChatViewController.m
代码拷走直接用
#pragma mark - EMUserListViewControllerDelegate
- (void)userListViewController:(EaseUsersListViewController *)userListViewController
didSelectUserModel:(id<IUserModel>)userModel
{
if (!self.messageModel) {
return;
}
if (self.messageModel.bodyType == EMMessageBodyTypeText) {
EMMessage *message = [EaseSDKHelper sendTextMessage:self.messageModel.text to:userModel.buddy messageType:EMChatTypeChat messageExt:self.messageModel.message.ext];
__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
// NSMutableArray *array = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
UIViewController *chatController = nil;
#ifdef REDPACKET_AVALABLE
chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
chatController = [[ChatViewController alloc]
initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? [userModel.nickname copy] : [userModel.buddy copy];
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakself.navigationController setViewControllers:array animated:YES];
[weakself.navigationController popViewControllerAnimated:YES];
} else {
[self showHudInView:self.view hint:Localized(@"transpondFail")];
}
}];
} else if (self.messageModel.bodyType == EMMessageBodyTypeImage) {
[self showHudInView:self.view hint:Localized(@"transponding")];
__weak typeof(self) weakSelf = self;
NSString *localPath = [(EMImageMessageBody *)self.messageModel.message.body thumbnailLocalPath];
//////////////////////////解决转发问题的代码////////////////////////////
localPath = [[NSUserDefaults standardUserDefaults] valueForKey:@"imgTosand"];
//////////////////////////解决转发问题的代码//////////////////////////
UIImage *image = [UIImage imageWithContentsOfFile:localPath];
void (^block)() = ^(EMMessage *message){
EMImageMessageBody *imgBody = (EMImageMessageBody *)message.body;
NSString *from = [[EMClient sharedClient] currentUsername];
EMImageMessageBody *newBody = [[EMImageMessageBody alloc] initWithData:nil thumbnailData:[NSData dataWithContentsOfFile:imgBody.thumbnailLocalPath]];
newBody.thumbnailLocalPath = imgBody.thumbnailLocalPath;
newBody.thumbnailRemotePath = imgBody.thumbnailRemotePath;
newBody.remotePath = imgBody.remotePath;
EMMessage *newMsg = [[EMMessage alloc] initWithConversationID:userModel.buddy from:from to:userModel.buddy body:newBody ext:message.ext];
// newMsg.chatType = message.chatType;//此为环信代码
newMsg.chatType = EMChatTypeChat;//这里是我加的
[[EMClient sharedClient].chatManager sendMessage:newMsg progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}
[(EMImageMessageBody *)message.body setLocalPath:imgBody.localPath];
[[EMClient sharedClient].chatManager updateMessage:message completion:nil];

// NSMutableArray *array = [NSMutableArray arrayWithArray:[weakSelf.navigationController viewControllers]];

#ifdef REDPACKET_AVALABLE
RedPacketChatViewController *chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
ChatViewController *chatController = [[ChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? userModel.nickname : userModel.buddy;
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakSelf.navigationController setViewControllers:array animated:YES];
[weakSelf.navigationController popViewControllerAnimated:YES];//转发完跳回去
}];
};

if (!image) {
[[EMClient sharedClient].chatManager downloadMessageThumbnail:self.messageModel.message progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}

block(message);
}];
} else {
block(self.messageModel.message);
}
}
}
上面一定要判断一下消息体类型,只有消息体为图片类型(EMMessageBodyTypeImage)才需要保存图片本地。如果不做判断的话,点击气泡马上崩掉。
个人感觉虽然能解决图片转发的问题,但并不是最好的解决办法,虽然对环信demo的代码改动最少。有更好的办法,欢迎在评论区交流。
(本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢)
3
回复

android 异地登录问题,杀进程后,再次进入程序提示异地登录,求解 android异地登录 即时通讯

baoshu 回复了问题 • 2 人关注 • 358 次浏览 • 2017-04-13 16:08 • 来自相关话题

1
回复

环信及时通信,基础版日活30万怎么收费,专业版怎么收费(不详细官网) 即时通讯 收费

donghai 回复了问题 • 2 人关注 • 497 次浏览 • 2016-12-05 18:01 • 来自相关话题

6
回复

【环信官方外包一个项目】利用环信IM实现一个在线白板 环信

ppdd8951 回复了问题 • 7 人关注 • 1441 次浏览 • 2017-11-15 14:42 • 来自相关话题

1
评论

【开源项目】一个基于环信IM开发的开源的私密社交APP-Baby 即时通信 Baby

seven。 发表了文章 • 1473 次浏览 • 2017-08-31 14:49 • 来自相关话题

Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。先上效果图




开屏页的登录和注册




编辑个人信息




相册页面




首页的Moment
版本更新:
 
version 1.6
加入Tinker 热修复更新部分依赖修复大量细节问题,加入部分注释

version 1.5
bug fixupdate sth

version 1.4
增加长按删除功能优化Rxbus订阅加载数据外国友人优化的一些细节等等

version 1.3
增加了评论功能优化了相册加载修复了一些内存泄漏等等

version 1.2
修复了一些Bug把登陆注册事件换了个Zip操作符更符合流的思想

version 1.1
修复了主页背景无法切换的问题修复了聊天推送的问题修了语音视频的问题做了一些细节修改
Download



安装包下载体验
 
github地址
github源码
 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。Thanks for improving my code m-ezzat.

Contacts
Email:379489343zhi@gmail.comQQ:379489343
 Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 查看全部
   Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。
先上效果图

003.png

开屏页的登录和注册


001.png

编辑个人信息


002.png

相册页面


004.png

首页的Moment


版本更新:
 
version 1.6
  1. 加入Tinker 热修复
  2. 更新部分依赖
  3. 修复大量细节问题,加入部分注释


version 1.5
  1. bug fix
  2. update sth


version 1.4
  1. 增加长按删除功能
  2. 优化Rxbus订阅加载数据
  3. 外国友人优化的一些细节
  4. 等等


version 1.3
  1. 增加了评论功能
  2. 优化了相册加载
  3. 修复了一些内存泄漏
  4. 等等


version 1.2
  1. 修复了一些Bug
  2. 把登陆注册事件换了个Zip操作符更符合流的思想


version 1.1
  1. 修复了主页背景无法切换的问题
  2. 修复了聊天推送的问题
  3. 修了语音视频的问题
  4. 做了一些细节修改

Download

 
github地址

 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
  • 感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。
  • Thanks for improving my code m-ezzat.


Contacts

 
Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
6
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 7371 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
6
回复

【环信官方外包一个项目】利用环信IM实现一个在线白板 环信

回复

ppdd8951 回复了问题 • 7 人关注 • 1441 次浏览 • 2017-11-15 14:42 • 来自相关话题

1
评论

【开源项目】一个基于环信IM开发的开源的私密社交APP-Baby 即时通信 Baby

seven。 发表了文章 • 1473 次浏览 • 2017-08-31 14:49 • 来自相关话题

Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。先上效果图




开屏页的登录和注册




编辑个人信息




相册页面




首页的Moment
版本更新:
 
version 1.6
加入Tinker 热修复更新部分依赖修复大量细节问题,加入部分注释

version 1.5
bug fixupdate sth

version 1.4
增加长按删除功能优化Rxbus订阅加载数据外国友人优化的一些细节等等

version 1.3
增加了评论功能优化了相册加载修复了一些内存泄漏等等

version 1.2
修复了一些Bug把登陆注册事件换了个Zip操作符更符合流的思想

version 1.1
修复了主页背景无法切换的问题修复了聊天推送的问题修了语音视频的问题做了一些细节修改
Download



安装包下载体验
 
github地址
github源码
 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。Thanks for improving my code m-ezzat.

Contacts
Email:379489343zhi@gmail.comQQ:379489343
 Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 查看全部
   Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。
先上效果图

003.png

开屏页的登录和注册


001.png

编辑个人信息


002.png

相册页面


004.png

首页的Moment


版本更新:
 
version 1.6
  1. 加入Tinker 热修复
  2. 更新部分依赖
  3. 修复大量细节问题,加入部分注释


version 1.5
  1. bug fix
  2. update sth


version 1.4
  1. 增加长按删除功能
  2. 优化Rxbus订阅加载数据
  3. 外国友人优化的一些细节
  4. 等等


version 1.3
  1. 增加了评论功能
  2. 优化了相册加载
  3. 修复了一些内存泄漏
  4. 等等


version 1.2
  1. 修复了一些Bug
  2. 把登陆注册事件换了个Zip操作符更符合流的思想


version 1.1
  1. 修复了主页背景无法切换的问题
  2. 修复了聊天推送的问题
  3. 修了语音视频的问题
  4. 做了一些细节修改

Download

 
github地址

 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
  • 感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。
  • Thanks for improving my code m-ezzat.


Contacts

 
Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
6
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 7371 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
3
回复

android 异地登录问题,杀进程后,再次进入程序提示异地登录,求解 android异地登录 即时通讯

回复

baoshu 回复了问题 • 2 人关注 • 358 次浏览 • 2017-04-13 16:08 • 来自相关话题

1
回复

环信及时通信,基础版日活30万怎么收费,专业版怎么收费(不详细官网) 即时通讯 收费

回复

donghai 回复了问题 • 2 人关注 • 497 次浏览 • 2016-12-05 18:01 • 来自相关话题

6
回复

【环信官方外包一个项目】利用环信IM实现一个在线白板 环信

回复

ppdd8951 回复了问题 • 7 人关注 • 1441 次浏览 • 2017-11-15 14:42 • 来自相关话题

1
评论

【开源项目】一个基于环信IM开发的开源的私密社交APP-Baby 即时通信 Baby

seven。 发表了文章 • 1473 次浏览 • 2017-08-31 14:49 • 来自相关话题

Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。先上效果图




开屏页的登录和注册




编辑个人信息




相册页面




首页的Moment
版本更新:
 
version 1.6
加入Tinker 热修复更新部分依赖修复大量细节问题,加入部分注释

version 1.5
bug fixupdate sth

version 1.4
增加长按删除功能优化Rxbus订阅加载数据外国友人优化的一些细节等等

version 1.3
增加了评论功能优化了相册加载修复了一些内存泄漏等等

version 1.2
修复了一些Bug把登陆注册事件换了个Zip操作符更符合流的思想

version 1.1
修复了主页背景无法切换的问题修复了聊天推送的问题修了语音视频的问题做了一些细节修改
Download



安装包下载体验
 
github地址
github源码
 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。Thanks for improving my code m-ezzat.

Contacts
Email:379489343zhi@gmail.comQQ:379489343
 Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 查看全部
   Baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。
先上效果图

003.png

开屏页的登录和注册


001.png

编辑个人信息


002.png

相册页面


004.png

首页的Moment


版本更新:
 
version 1.6
  1. 加入Tinker 热修复
  2. 更新部分依赖
  3. 修复大量细节问题,加入部分注释


version 1.5
  1. bug fix
  2. update sth


version 1.4
  1. 增加长按删除功能
  2. 优化Rxbus订阅加载数据
  3. 外国友人优化的一些细节
  4. 等等


version 1.3
  1. 增加了评论功能
  2. 优化了相册加载
  3. 修复了一些内存泄漏
  4. 等等


version 1.2
  1. 修复了一些Bug
  2. 把登陆注册事件换了个Zip操作符更符合流的思想


version 1.1
  1. 修复了主页背景无法切换的问题
  2. 修复了聊天推送的问题
  3. 修了语音视频的问题
  4. 做了一些细节修改

Download

 
github地址

 Development Environment & Library
 
MVP

这个项目是基于MVP框架写的(大体上,聊天那块直接用环信的了),大部分Base类参考FastAndroid里边的基类,参考这个很快就能布好基本的MVP架构。本来是有考虑过MVVM后来想想还是先学习一下MVP吧,看过几个MVVM项目感觉还是挺好用的,不过还是BETA版不知道有没有什么坑。

Material Design

早就手痒想体验一把V7包里边的各种控件了,特喜欢coordinatorlayout和collapsingtoolbarlayout的互动让Tollbar隐藏又现的感觉,但是看起来好看还是要点代价的,在这里捣鼓了不少时间,尤其是collapsingtoolbarlayout的Expanded固定让我Google了好久,因为用英文搜索可能我表达的不太好,最后竟然是一句nestedScroll(false)就可以了。。。。 还是感谢Stackoveflow里边的大腿吧。

Dagger2

依赖注入Dagger2,也是我早就想用的一个框架了,理由是各种配合Mvp十分方便和好用,渐渐也能体会到一次注入到处可用的快感。不过一个新技术真的学习成本,国内没什么中文文档介绍,看国外的看的云里雾里。原理看的明白,用起来好像不太知道如何使用,尤其是在@inject之后对象,也可以在别的地方Inject,原本是被Inject方后来也成了Module提供方。虽然到最后原理还不是特别明白这里,但还是不阻碍用起来的快感。

Realm

一开始被新技术吸引到的是不会放过任何新东西的包括Realm,不过进了坑不代表这个坑可以跳阿。由于我这次用到了leacncloud,Realm感觉会和LeanCloud的子类化冲突让你只能选择其中之一,不过这个也算了,Leancloud提供了类似Map的Put方法也可以接受就是麻烦了点。但是被坑到的地方是Realm所谓的自动数据同步竟然是一改就是改真实的数据,并不是数据的拷贝。。。感觉和我使用到要缓存的数据有点冲突,因为这个Moment里边的项是有点赞的,点赞要修改当前Recycleview的数据(修改数据要开事务)。修改数据后会出现一些很奇怪的现象,不在Recycleview当前Item会跳到当前Item,点赞的动画也会消失。。。真的是想破脑袋也解决不了,就直接跳坑了。最后感觉这个Realm在保存不跟服务器需要同步的数据会好点。

LeanCloud

用LeanCloud是因为在知乎太多吹它的人了而且它的确在BAAS这方面功能比较多(后来才发现即时通信没有语音和视频),所以就尝试使用了,SDK整体来说是不错的都挺好用的,满足了我对存储方面的要求。不过就在我开发的这几天,貌似稳定性没有想象中那么好,好几次上传个头像都会SocketTimeOut,查询也会有点慢,不过还好都在接受范围内(不过要是到了收费的标准我就接受不了)。

环信

baby这个开源项目基于环信IM开发,刚开始选择IM的时候,看到知乎上黑环信的人挺多的,就亲自下载了几家IM的demo源码跑一遍,从功能、集成难易和消息稳定几个方面对比,最终还是选择了环信。集成这方面环信做的真不错,尤其是有了EaseUi这个包,基本上一天就能集成完毕。项目运行至今没有出现过什么问题,发送消息挺稳定的。

Rxjava、RxAndroid

Rxjava我从第一眼看到了就喜欢上了(个人特喜欢那种通过.设置完成的感觉),接触也有几个月,一开始就和Retrofit 、Okhttp一起使用。学习成本还是要有的阿,看了不知道多少篇关于Rxjava使用的文章和例子,对里边的操作符也仅仅停留在那几个最常用的,其他一大堆好多都没用过,看来还用得不够。

Glide

Glide也是一个后来居上让我喜欢的图片加载库,一开始我喜欢picasso 是觉得轻巧而且好用有保证(主要是我偶像Jake Wharton主导,有加成),慢慢觉得Picasso对内存没有Glide来的友好,Glide在加载速度方面也领先,虽然整个库代码量是Picasso的几倍,但是比起重要的内存和用户体验来说还是Glide的领先一筹。

Butterknife、Ucrop等等

当让还有其他的一些润色的轮子啦,不过不是那么重要就不一一感谢啦。

Thanks
  • 感谢Github、LeanCloud、环信、还有造那么多轮子给我们用的Square FaceBook Google的大大们。
  • Thanks for improving my code m-ezzat.


Contacts

 
Copyright 2016 Roger ou

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
6
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 7371 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
0
评论

【环信征文】集成环信,并实现消息免打扰 即时通讯 环信 消息免打扰 IM iOS

cokeyer 发表了文章 • 139 次浏览 • 2017-09-14 11:27 • 来自相关话题

现在大多数社交app都有消息免打扰功能,因为环信SDK主要是对即时通讯模块进行封装,因此如果要实现消息免打扰功能则需要开发者自己对此功能逻辑进行处理。
先解释一下,本人项目中对消息免打扰功能的定义:正常情况下,我们收到的每一条聊天消息都会收到小红点、声音、震动的提示。如果对某个好友设置消息免打扰功能,则只提示小红点,声音和震动则不再提示。

下面简述结合环信SDK时,此功能的实现方法。
环信将即时聊天的所有功能分为四大模块进行管理://聊天模块:
[EMClient sharedClient].chatManager
//好友模块 :
[EMClient sharedClient].contactManager
//群组模块 :
[EMClient sharedClient].groupManager
//聊天室模块:
[EMClient sharedClient].roomManager消息免打扰的功能借助聊天模块[EMClient sharedClient].chatManager的API就能实现。
一般来说,在app内不管当前在哪个界面,只要收到消息都需要被判断是否需要免打扰,因此可以在appdelegate里写如下代码,app通常都有tabBarController,那么也可以让tabBarController来实现如下代码。
首先让控制器tabBarController遵守代理EMChatManagerDelegate
然后成为代理
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

EMChatManagerDelegate中有一个代理方法- (void)didReceiveMessages:(NSArray *)aMessages;实现此代理方法- (void)didReceiveMessages:(NSArray *)aMessages{
[self setupUnreadMessageCount];
EMMessage *message = aMessages[0];
NSString *sendPerson = message.from;
BHNavigatiomController *imNaviCV = self.viewControllers[1];
BHConversationListController *converCV = imNaviCV.viewControllers[0];
[converCV refreshDataSource];//只要收到消息就从服务器拿
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {
if ([hx_name isEqualToString:sendPerson]) {
return;
}
}
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
}方法中有一个aMessages参数。这个参数是一个消息组,每一个元素都是EMMessage类型的实例。
因为通常都只有一条信息。因此只需要取出EMMessage *message = aMessages[0];
EMMessage类中有许多属性,因此根据一条信息基本可以获取想知道的所有信息。这里我们只需要知道此消息的发送方即可,也就是发送方的环信idNSString *sendPerson = message.from;下面代码中有一AppEngine.IMDataCent.data_ExcuseFriendsData,这个数组里面装的都是已经被设置消息免打扰的好友的环信id,是请求自己服务器获得的,获取的代码最好在一打开app时,就及时获取到。然后根据对好友免打扰设置的操作,访问后台接口进行增删,并刷新数组与后端保持一致即可。
通过[hx_name isEqualToString:sendPerson],遍历免打扰数组与当前消息的发送方环信id,就可以知道是否需要免打扰了。
 
下面附上完整代码//
// BHTabBarController.m
// ShangHeYiYang
//
// Created by LiBohan on 2017/8/24.
// Copyright © 2017年 xxxxx. All rights reserved.
//


//两次提示的默认间隔
static const CGFloat kDefaultPlaySoundInterval = 3.0;
static NSString *kMessageType = @"MessageType";
static NSString *kConversationChatter = @"ConversationChatter";
static NSString *kGroupName = @"GroupName";


#import "BHTabBarController.h"
#import <UserNotifications/UserNotifications.h>
#import "BHConversationListController.h"

@interface BHTabBarController ()<EMChatManagerDelegate,EMContactManagerDelegate>

@property (strong, nonatomic) NSDate *lastPlaySoundDate;

@end

@implementation BHTabBarController

- (void)viewDidLoad {
[super viewDidLoad];

[DemoCallManager sharedManager].mainController = self;

[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

[[EMClient sharedClient].contactManager addDelegate:self delegateQueue:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupUnreadMessageCount) name:@"setupUnreadMessageCount" object:nil];

}

-(void)friendshipDidRemoveByUser:(NSString *)aUsername{

// __weak __typeof(&*self)weakSelf = self;

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:contactReloadData object:nil];
});


// if ([AppEngine.mainDataCent.data_UserData.data_HxID isEqualToString:aUsername]) {


//删除好友成功后,再删除聊天会话
[[EMClient sharedClient].chatManager deleteConversation:aUsername isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError) {

if (!aError) {
//删除聊天会话,成功后,刷新聊天会话列表
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:talkBtnClickThenUpdateConversionlist object:nil];
});


}

}];
}

// 统计未读消息数
-(void)setupUnreadMessageCount
{
NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for (EMConversation *conversation in conversations) {
unreadCount += conversation.unreadMessagesCount;
}

NSArray *tabBarItems = self.tabBar.items;

UITabBarItem *conlistTabBarItem = [tabBarItems objectAtIndex:1];
if (unreadCount > 0) {
conlistTabBarItem.badgeValue = [NSString stringWithFormat:@"%i",(int)unreadCount];
}else{
conlistTabBarItem.badgeValue = nil;
}


// UIApplication *application = [UIApplication sharedApplication];
// [application setApplicationIconBadgeNumber:unreadCount];
}


- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages {
for (EMMessage *message in aCmdMessages) {
EMCmdMessageBody *body = (EMCmdMessageBody *)message.body;
NSLog(@"收到的action是 -- %@",body.action);


if (body.action == nil) {

return;

}

NSData *jsonData = [body.action dataUsingEncoding:NSUTF8StringEncoding];

NSError *err;

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

if(err) {

NSLog(@"json解析失败:%@",err);

return;

}

NSNumber *num = dic[@"count"];

NSString *str = [num stringValue];

[AppEngine.IMDataCent requestUpdateUnreadNumberWithUnreadNumber:str];

}


}

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

// [self refresh];

[self setupUnreadMessageCount];

EMMessage *message = aMessages[0];

NSString *sendPerson = message.from;

BHNavigatiomController *imNaviCV = self.viewControllers[1];

BHConversationListController *converCV = imNaviCV.viewControllers[0];

// [converCV refresh];

[converCV refreshDataSource];//只要收到消息就从服务器拿

UIApplicationState state = [[UIApplication sharedApplication] applicationState];

for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {

if ([hx_name isEqualToString:sendPerson]) {
return;
}

}

switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}

}

- (void)playSoundAndVibration{
NSTimeInterval timeInterval = [[NSDate date]
timeIntervalSinceDate:self.lastPlaySoundDate];
if (timeInterval < kDefaultPlaySoundInterval) {
//如果距离上次响铃和震动时间太短, 则跳过响铃
NSLog(@"skip ringing & vibration %@, %@", [NSDate date], self.lastPlaySoundDate);
return;
}

//保存最后一次响铃时间
self.lastPlaySoundDate = [NSDate date];

// 收到消息时,播放音频
[[EMCDDeviceManager sharedInstance] playNewMessageSound];
// 收到消息时,震动
[[EMCDDeviceManager sharedInstance] playVibration];
}


- (void)showNotificationWithMessage:(EMMessage *)message
{
EMPushOptions *options = [[EMClient sharedClient] pushOptions];
NSString *alertBody = nil;
if (options.displayStyle == EMPushDisplayStyleMessageSummary) {
EMMessageBody *messageBody = message.body;
NSString *messageStr = nil;
switch (messageBody.type) {
case EMMessageBodyTypeText:
{
messageStr = ((EMTextMessageBody *)messageBody).text;
}
break;
case EMMessageBodyTypeImage:
{
messageStr = NSLocalizedString(@"message.image", @"Image");
}
break;
case EMMessageBodyTypeLocation:
{
messageStr = NSLocalizedString(@"message.location", @"Location");
}
break;
case EMMessageBodyTypeVoice:
{
messageStr = NSLocalizedString(@"message.voice", @"Voice");
}
break;
case EMMessageBodyTypeVideo:{
messageStr = NSLocalizedString(@"message.video", @"Video");
}
break;
default:
break;
}

do {
// NSString *title = [[UserProfileManager sharedInstance] getNickNameWithUsername:message.from];
NSString *title = @"大佬";

if (message.chatType == EMChatTypeGroupChat) {
NSDictionary *ext = message.ext;
if (ext && ext[kGroupMessageAtList]) {
id target = ext[kGroupMessageAtList];
if ([target isKindOfClass:[NSString class]]) {
if ([kGroupMessageAtAll compare:target options:NSCaseInsensitiveSearch] == NSOrderedSame) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
else if ([target isKindOfClass:[NSArray class]]) {
NSArray *atTargets = (NSArray*)target;
if ([atTargets containsObject:[EMClient sharedClient].currentUsername]) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
}
NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
for (EMGroup *group in groupArray) {
if ([group.groupId isEqualToString:message.conversationId]) {
title = [NSString stringWithFormat:@"%@(%@)", message.from, group.subject];
break;
}
}
}
else if (message.chatType == EMChatTypeChatRoom)
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
NSMutableDictionary *chatrooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
NSString *chatroomName = [chatrooms objectForKey:message.conversationId];
if (chatroomName)
{
title = [NSString stringWithFormat:@"%@(%@)", message.from, chatroomName];
}
}

alertBody = [NSString stringWithFormat:@"%@:%@", title, messageStr];
} while (0);
}
else{
// alertBody = NSLocalizedString(@"receiveMessage", @"you have a new message");
alertBody = @"您有一条消息";
}

NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.lastPlaySoundDate];
BOOL playSound = NO;
if (!self.lastPlaySoundDate || timeInterval >= kDefaultPlaySoundInterval) {
self.lastPlaySoundDate = [NSDate date];
playSound = YES;
}

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:message.chatType] forKey:kMessageType];
[userInfo setObject:message.conversationId forKey:kConversationChatter];

//发送本地推送
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
if (playSound) {
content.sound = [UNNotificationSound defaultSound];
}
content.body =alertBody;
content.userInfo = userInfo;
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.messageId content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate date]; //触发通知的时间
notification.alertBody = alertBody;
notification.alertAction = NSLocalizedString(@"open", @"Open");
notification.timeZone = [NSTimeZone defaultTimeZone];
if (playSound) {
notification.soundName = UILocalNotificationDefaultSoundName;
}
notification.userInfo = userInfo;

//发送通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}

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

NSLog(@"sf");
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


 
 
本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂 查看全部
现在大多数社交app都有消息免打扰功能,因为环信SDK主要是对即时通讯模块进行封装,因此如果要实现消息免打扰功能则需要开发者自己对此功能逻辑进行处理。
先解释一下,本人项目中对消息免打扰功能的定义:正常情况下,我们收到的每一条聊天消息都会收到小红点、声音、震动的提示。如果对某个好友设置消息免打扰功能,则只提示小红点,声音和震动则不再提示。

下面简述结合环信SDK时,此功能的实现方法。
环信将即时聊天的所有功能分为四大模块进行管理:
//聊天模块:
[EMClient sharedClient].chatManager
//好友模块 :
[EMClient sharedClient].contactManager
//群组模块 :
[EMClient sharedClient].groupManager
//聊天室模块:
[EMClient sharedClient].roomManager
消息免打扰的功能借助聊天模块[EMClient sharedClient].chatManager的API就能实现。
一般来说,在app内不管当前在哪个界面,只要收到消息都需要被判断是否需要免打扰,因此可以在appdelegate里写如下代码,app通常都有tabBarController,那么也可以让tabBarController来实现如下代码。
首先让控制器tabBarController遵守代理EMChatManagerDelegate
然后成为代理
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

EMChatManagerDelegate中有一个代理方法
- (void)didReceiveMessages:(NSArray *)aMessages;
实现此代理方法
- (void)didReceiveMessages:(NSArray *)aMessages{
[self setupUnreadMessageCount];
EMMessage *message = aMessages[0];
NSString *sendPerson = message.from;
BHNavigatiomController *imNaviCV = self.viewControllers[1];
BHConversationListController *converCV = imNaviCV.viewControllers[0];
[converCV refreshDataSource];//只要收到消息就从服务器拿
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {
if ([hx_name isEqualToString:sendPerson]) {
return;
}
}
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
}
方法中有一个aMessages参数。这个参数是一个消息组,每一个元素都是EMMessage类型的实例。
因为通常都只有一条信息。因此只需要取出EMMessage *message = aMessages[0];
EMMessage类中有许多属性,因此根据一条信息基本可以获取想知道的所有信息。这里我们只需要知道此消息的发送方即可,也就是发送方的环信idNSString *sendPerson = message.from;下面代码中有一AppEngine.IMDataCent.data_ExcuseFriendsData,这个数组里面装的都是已经被设置消息免打扰的好友的环信id,是请求自己服务器获得的,获取的代码最好在一打开app时,就及时获取到。然后根据对好友免打扰设置的操作,访问后台接口进行增删,并刷新数组与后端保持一致即可。
通过[hx_name isEqualToString:sendPerson],遍历免打扰数组与当前消息的发送方环信id,就可以知道是否需要免打扰了。
 
下面附上完整代码
//
// BHTabBarController.m
// ShangHeYiYang
//
// Created by LiBohan on 2017/8/24.
// Copyright © 2017年 xxxxx. All rights reserved.
//


//两次提示的默认间隔
static const CGFloat kDefaultPlaySoundInterval = 3.0;
static NSString *kMessageType = @"MessageType";
static NSString *kConversationChatter = @"ConversationChatter";
static NSString *kGroupName = @"GroupName";


#import "BHTabBarController.h"
#import <UserNotifications/UserNotifications.h>
#import "BHConversationListController.h"

@interface BHTabBarController ()<EMChatManagerDelegate,EMContactManagerDelegate>

@property (strong, nonatomic) NSDate *lastPlaySoundDate;

@end

@implementation BHTabBarController

- (void)viewDidLoad {
[super viewDidLoad];

[DemoCallManager sharedManager].mainController = self;

[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

[[EMClient sharedClient].contactManager addDelegate:self delegateQueue:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupUnreadMessageCount) name:@"setupUnreadMessageCount" object:nil];

}

-(void)friendshipDidRemoveByUser:(NSString *)aUsername{

// __weak __typeof(&*self)weakSelf = self;

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:contactReloadData object:nil];
});


// if ([AppEngine.mainDataCent.data_UserData.data_HxID isEqualToString:aUsername]) {


//删除好友成功后,再删除聊天会话
[[EMClient sharedClient].chatManager deleteConversation:aUsername isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError) {

if (!aError) {
//删除聊天会话,成功后,刷新聊天会话列表
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:talkBtnClickThenUpdateConversionlist object:nil];
});


}

}];
}

// 统计未读消息数
-(void)setupUnreadMessageCount
{
NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for (EMConversation *conversation in conversations) {
unreadCount += conversation.unreadMessagesCount;
}

NSArray *tabBarItems = self.tabBar.items;

UITabBarItem *conlistTabBarItem = [tabBarItems objectAtIndex:1];
if (unreadCount > 0) {
conlistTabBarItem.badgeValue = [NSString stringWithFormat:@"%i",(int)unreadCount];
}else{
conlistTabBarItem.badgeValue = nil;
}


// UIApplication *application = [UIApplication sharedApplication];
// [application setApplicationIconBadgeNumber:unreadCount];
}


- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages {
for (EMMessage *message in aCmdMessages) {
EMCmdMessageBody *body = (EMCmdMessageBody *)message.body;
NSLog(@"收到的action是 -- %@",body.action);


if (body.action == nil) {

return;

}

NSData *jsonData = [body.action dataUsingEncoding:NSUTF8StringEncoding];

NSError *err;

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

if(err) {

NSLog(@"json解析失败:%@",err);

return;

}

NSNumber *num = dic[@"count"];

NSString *str = [num stringValue];

[AppEngine.IMDataCent requestUpdateUnreadNumberWithUnreadNumber:str];

}


}

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

// [self refresh];

[self setupUnreadMessageCount];

EMMessage *message = aMessages[0];

NSString *sendPerson = message.from;

BHNavigatiomController *imNaviCV = self.viewControllers[1];

BHConversationListController *converCV = imNaviCV.viewControllers[0];

// [converCV refresh];

[converCV refreshDataSource];//只要收到消息就从服务器拿

UIApplicationState state = [[UIApplication sharedApplication] applicationState];

for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {

if ([hx_name isEqualToString:sendPerson]) {
return;
}

}

switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}

}

- (void)playSoundAndVibration{
NSTimeInterval timeInterval = [[NSDate date]
timeIntervalSinceDate:self.lastPlaySoundDate];
if (timeInterval < kDefaultPlaySoundInterval) {
//如果距离上次响铃和震动时间太短, 则跳过响铃
NSLog(@"skip ringing & vibration %@, %@", [NSDate date], self.lastPlaySoundDate);
return;
}

//保存最后一次响铃时间
self.lastPlaySoundDate = [NSDate date];

// 收到消息时,播放音频
[[EMCDDeviceManager sharedInstance] playNewMessageSound];
// 收到消息时,震动
[[EMCDDeviceManager sharedInstance] playVibration];
}


- (void)showNotificationWithMessage:(EMMessage *)message
{
EMPushOptions *options = [[EMClient sharedClient] pushOptions];
NSString *alertBody = nil;
if (options.displayStyle == EMPushDisplayStyleMessageSummary) {
EMMessageBody *messageBody = message.body;
NSString *messageStr = nil;
switch (messageBody.type) {
case EMMessageBodyTypeText:
{
messageStr = ((EMTextMessageBody *)messageBody).text;
}
break;
case EMMessageBodyTypeImage:
{
messageStr = NSLocalizedString(@"message.image", @"Image");
}
break;
case EMMessageBodyTypeLocation:
{
messageStr = NSLocalizedString(@"message.location", @"Location");
}
break;
case EMMessageBodyTypeVoice:
{
messageStr = NSLocalizedString(@"message.voice", @"Voice");
}
break;
case EMMessageBodyTypeVideo:{
messageStr = NSLocalizedString(@"message.video", @"Video");
}
break;
default:
break;
}

do {
// NSString *title = [[UserProfileManager sharedInstance] getNickNameWithUsername:message.from];
NSString *title = @"大佬";

if (message.chatType == EMChatTypeGroupChat) {
NSDictionary *ext = message.ext;
if (ext && ext[kGroupMessageAtList]) {
id target = ext[kGroupMessageAtList];
if ([target isKindOfClass:[NSString class]]) {
if ([kGroupMessageAtAll compare:target options:NSCaseInsensitiveSearch] == NSOrderedSame) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
else if ([target isKindOfClass:[NSArray class]]) {
NSArray *atTargets = (NSArray*)target;
if ([atTargets containsObject:[EMClient sharedClient].currentUsername]) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
}
NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
for (EMGroup *group in groupArray) {
if ([group.groupId isEqualToString:message.conversationId]) {
title = [NSString stringWithFormat:@"%@(%@)", message.from, group.subject];
break;
}
}
}
else if (message.chatType == EMChatTypeChatRoom)
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
NSMutableDictionary *chatrooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
NSString *chatroomName = [chatrooms objectForKey:message.conversationId];
if (chatroomName)
{
title = [NSString stringWithFormat:@"%@(%@)", message.from, chatroomName];
}
}

alertBody = [NSString stringWithFormat:@"%@:%@", title, messageStr];
} while (0);
}
else{
// alertBody = NSLocalizedString(@"receiveMessage", @"you have a new message");
alertBody = @"您有一条消息";
}

NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.lastPlaySoundDate];
BOOL playSound = NO;
if (!self.lastPlaySoundDate || timeInterval >= kDefaultPlaySoundInterval) {
self.lastPlaySoundDate = [NSDate date];
playSound = YES;
}

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:message.chatType] forKey:kMessageType];
[userInfo setObject:message.conversationId forKey:kConversationChatter];

//发送本地推送
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
if (playSound) {
content.sound = [UNNotificationSound defaultSound];
}
content.body =alertBody;
content.userInfo = userInfo;
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.messageId content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate date]; //触发通知的时间
notification.alertBody = alertBody;
notification.alertAction = NSLocalizedString(@"open", @"Open");
notification.timeZone = [NSTimeZone defaultTimeZone];
if (playSound) {
notification.soundName = UILocalNotificationDefaultSoundName;
}
notification.userInfo = userInfo;

//发送通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}

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

NSLog(@"sf");
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


 
 
本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂

qrcode_for_gh_bc92a063b4a2_430.jpg
0
评论

【环信征文】基于环信开发一个医疗APP-Ⅰ iOS 环信 即时通讯 IM

cokeyer 发表了文章 • 163 次浏览 • 2017-09-12 17:56 • 来自相关话题

公司又有了新项目,依然是含有即时通讯功能模块的项目。在经历了上个项目对环信sdk的集成后,对环信EaseUI有了大概的了解。这次果断还是集成环信,一回生二回熟,最主要的还是对环信IM稳定性非常放心!项目医疗类的项目,角色分医生和患者,双方都可主动发起会话,但如果是患者找医生聊天,必须先经过预约,并只能在预约时间区间内才能和医生发送聊天消息、图片、语音、实时音视频。项目基于环信最V3.3.4版本开发,首先在会话界面自定义一个类比如BHChatViewController,继承自EaseMessageViewController类,基本上一个简单的界面就有了。以下就是EaseMessageViewController类的发送各种消息的方法,那么根据需要只需重写以下方法即可。/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@result
*/
- (void)sendTextMessage:(NSString *)text;

/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@param ext 扩展信息
@result
*/
- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext;

/*!
@method
@brief 发送图片消息
@discussion
@param image 发送图片
@result
*/
- (void)sendImageMessage:(UIImage *)image;

/*!
@method
@brief 发送位置消息
@discussion
@param latitude 经度
@param longitude 纬度
@param address 地址
@result
*/
- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address;

/*!
@method
@brief 发送语音消息
@discussion
@param localPath 语音本地地址
@param duration 时长
@result
*/
- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration;

/*!
@method
@brief 发送视频消息
@discussion
@param url 视频url
@result
*/
- (void)sendVideoMessageWithURL:(NSURL *)url;我在对发送图片的操作进行处理的时候,发现当拍照发图时,走这个方法//当你拍照发图时,走这个方法
- (void)sendImageMessage:(UIImage *)image;但当从相册选择图片发送时,会发现,不走上面的方法了。
仔细检查代码发现走了EaseMessageViewController.m的如下方法





然而这个方法,环信并没有放在EaseMessageViewController.h成为公开方法。我们只需手动粘贴方法到.h,然后在自己的子类重写就可以了。

还有一个关于更多下图更多功能区域的问题





如上图所示,相册、拍照、视频等附加功能按钮,环信用EaseChatBarMoreView类来管理的。
如果需要增加功能按钮用这个方法/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;移除某个功能按钮用这个方法/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;修改一个功能按钮用这个方法/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;可当你添加按钮,或者修改按钮时,会发现按钮的名字设置不了
然后检查环信内部的实现,发现title的值在方法里根本就没用到?!
索性不用updateItemWithImage这个方法了,直接去改内部代码。
修改代码如下

到EaseChatBarMoreView.m修改- (void)setupSubviewsForType:(EMChatToolbarType)type方法。改动的部分在代码后面有标注。- (void)setupSubviewsForType:(EMChatToolbarType)type
{
//self.backgroundColor = [UIColor clearColor];
self.accessibilityIdentifier = @"more_view";

_scrollview = [[UIScrollView alloc] init];
_scrollview.pagingEnabled = YES;
_scrollview.showsHorizontalScrollIndicator = NO;
_scrollview.showsVerticalScrollIndicator = NO;
_scrollview.delegate = self;
[self addSubview:_scrollview];

_pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
_pageControl.numberOfPages = 1;
[self addSubview:_pageControl];

CGFloat insets = (self.frame.size.width - 4 * CHAT_BUTTON_SIZE) / 5;

_photoButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_photoButton setTitle:@"相册" forState:UIControlStateNormal];//改动
[_photoButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_photoButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_photoButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_photoButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_photoButton.accessibilityIdentifier = @"image";
[_photoButton setFrame:CGRectMake(insets, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photo"] forState:UIControlStateNormal];
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photoSelected"] forState:UIControlStateHighlighted];
[_photoButton addTarget:self action:@selector(photoAction) forControlEvents:UIControlEventTouchUpInside];
_photoButton.tag = MOREVIEW_BUTTON_TAG;
[_scrollview addSubview:_photoButton];

_locationButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_locationButton setTitle:@"位置" forState:UIControlStateNormal];//改动
[_locationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_locationButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_locationButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_locationButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_locationButton.accessibilityIdentifier = @"location";
[_locationButton setFrame:CGRectMake(insets * 2 + CHAT_BUTTON_SIZE, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_location"] forState:UIControlStateNormal];
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_locationSelected"] forState:UIControlStateHighlighted];
[_locationButton addTarget:self action:@selector(locationAction) forControlEvents:UIControlEventTouchUpInside];
_locationButton.tag = MOREVIEW_BUTTON_TAG + 1;
[_scrollview addSubview:_locationButton];

_takePicButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_takePicButton setTitle:@"拍照" forState:UIControlStateNormal];//改动
[_takePicButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_takePicButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_takePicButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_takePicButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_takePicButton setFrame:CGRectMake(insets * 3 + CHAT_BUTTON_SIZE * 2, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_camera"] forState:UIControlStateNormal];
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_cameraSelected"] forState:UIControlStateHighlighted];
[_takePicButton addTarget:self action:@selector(takePicAction) forControlEvents:UIControlEventTouchUpInside];
_takePicButton.tag = MOREVIEW_BUTTON_TAG + 2;
_maxIndex = 2;
[_scrollview addSubview:_takePicButton];

CGRect frame = self.frame;
if (type == EMChatToolbarTypeChat) {
frame.size.height = 150;
_audioCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_audioCallButton setTitle:@"语音" forState:UIControlStateNormal];//改动
[_audioCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_audioCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_audioCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_audioCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_audioCallButton setFrame:CGRectMake(insets * 4 + CHAT_BUTTON_SIZE * 3, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCall"] forState:UIControlStateNormal];
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCallSelected"] forState:UIControlStateHighlighted];
[_audioCallButton addTarget:self action:@selector(takeAudioCallAction) forControlEvents:UIControlEventTouchUpInside];
_audioCallButton.tag = MOREVIEW_BUTTON_TAG + 3;
[_scrollview addSubview:_audioCallButton];

_videoCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_videoCallButton setTitle:@"视频" forState:UIControlStateNormal];//改动
[_videoCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_videoCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_videoCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_videoCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_videoCallButton setFrame:CGRectMake(insets, 10 * 2 + CHAT_BUTTON_SIZE + 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCall"] forState:UIControlStateNormal];
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCallSelected"] forState:UIControlStateHighlighted];
[_videoCallButton addTarget:self action:@selector(takeVideoCallAction) forControlEvents:UIControlEventTouchUpInside];
_videoCallButton.tag =MOREVIEW_BUTTON_TAG + 4;
_maxIndex = 4;
[_scrollview addSubview:_videoCallButton];
}
else if (type == EMChatToolbarTypeGroup)
{
frame.size.height = 80;
}
self.frame = frame;
_scrollview.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_pageControl.frame = CGRectMake(0, CGRectGetHeight(frame) - 20, CGRectGetWidth(frame), 20);
_pageControl.hidden = _pageControl.numberOfPages<=1;
}




本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂





 
项目完整源码 查看全部
公司又有了新项目,依然是含有即时通讯功能模块的项目。在经历了上个项目对环信sdk的集成后,对环信EaseUI有了大概的了解。这次果断还是集成环信,一回生二回熟,最主要的还是对环信IM稳定性非常放心!
项目医疗类的项目,角色分医生和患者,双方都可主动发起会话,但如果是患者找医生聊天,必须先经过预约,并只能在预约时间区间内才能和医生发送聊天消息、图片、语音、实时音视频。项目基于环信最V3.3.4版本开发,首先在会话界面自定义一个类比如BHChatViewController,继承自EaseMessageViewController类,基本上一个简单的界面就有了。以下就是EaseMessageViewController类的发送各种消息的方法,那么根据需要只需重写以下方法即可。
/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@result
*/
- (void)sendTextMessage:(NSString *)text;

/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@param ext 扩展信息
@result
*/
- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext;

/*!
@method
@brief 发送图片消息
@discussion
@param image 发送图片
@result
*/
- (void)sendImageMessage:(UIImage *)image;

/*!
@method
@brief 发送位置消息
@discussion
@param latitude 经度
@param longitude 纬度
@param address 地址
@result
*/
- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address;

/*!
@method
@brief 发送语音消息
@discussion
@param localPath 语音本地地址
@param duration 时长
@result
*/
- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration;

/*!
@method
@brief 发送视频消息
@discussion
@param url 视频url
@result
*/
- (void)sendVideoMessageWithURL:(NSURL *)url;
我在对发送图片的操作进行处理的时候,发现当拍照发图时,走这个方法
//当你拍照发图时,走这个方法
- (void)sendImageMessage:(UIImage *)image;
但当从相册选择图片发送时,会发现,不走上面的方法了。
仔细检查代码发现走了EaseMessageViewController.m的如下方法

环信2.png

然而这个方法,环信并没有放在EaseMessageViewController.h成为公开方法。我们只需手动粘贴方法到.h,然后在自己的子类重写就可以了。

还有一个关于更多下图更多功能区域的问题

EaseChatBarMoreView.png

如上图所示,相册、拍照、视频等附加功能按钮,环信用EaseChatBarMoreView类来管理的。
如果需要增加功能按钮用这个方法
/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;
移除某个功能按钮用这个方法
/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;
修改一个功能按钮用这个方法
/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;
可当你添加按钮,或者修改按钮时,会发现按钮的名字设置不了
然后检查环信内部的实现,发现title的值在方法里根本就没用到?!
索性不用updateItemWithImage这个方法了,直接去改内部代码。
修改代码如下

到EaseChatBarMoreView.m修改- (void)setupSubviewsForType:(EMChatToolbarType)type方法。改动的部分在代码后面有标注。
- (void)setupSubviewsForType:(EMChatToolbarType)type
{
//self.backgroundColor = [UIColor clearColor];
self.accessibilityIdentifier = @"more_view";

_scrollview = [[UIScrollView alloc] init];
_scrollview.pagingEnabled = YES;
_scrollview.showsHorizontalScrollIndicator = NO;
_scrollview.showsVerticalScrollIndicator = NO;
_scrollview.delegate = self;
[self addSubview:_scrollview];

_pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
_pageControl.numberOfPages = 1;
[self addSubview:_pageControl];

CGFloat insets = (self.frame.size.width - 4 * CHAT_BUTTON_SIZE) / 5;

_photoButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_photoButton setTitle:@"相册" forState:UIControlStateNormal];//改动
[_photoButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_photoButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_photoButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_photoButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_photoButton.accessibilityIdentifier = @"image";
[_photoButton setFrame:CGRectMake(insets, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photo"] forState:UIControlStateNormal];
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photoSelected"] forState:UIControlStateHighlighted];
[_photoButton addTarget:self action:@selector(photoAction) forControlEvents:UIControlEventTouchUpInside];
_photoButton.tag = MOREVIEW_BUTTON_TAG;
[_scrollview addSubview:_photoButton];

_locationButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_locationButton setTitle:@"位置" forState:UIControlStateNormal];//改动
[_locationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_locationButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_locationButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_locationButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_locationButton.accessibilityIdentifier = @"location";
[_locationButton setFrame:CGRectMake(insets * 2 + CHAT_BUTTON_SIZE, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_location"] forState:UIControlStateNormal];
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_locationSelected"] forState:UIControlStateHighlighted];
[_locationButton addTarget:self action:@selector(locationAction) forControlEvents:UIControlEventTouchUpInside];
_locationButton.tag = MOREVIEW_BUTTON_TAG + 1;
[_scrollview addSubview:_locationButton];

_takePicButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_takePicButton setTitle:@"拍照" forState:UIControlStateNormal];//改动
[_takePicButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_takePicButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_takePicButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_takePicButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_takePicButton setFrame:CGRectMake(insets * 3 + CHAT_BUTTON_SIZE * 2, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_camera"] forState:UIControlStateNormal];
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_cameraSelected"] forState:UIControlStateHighlighted];
[_takePicButton addTarget:self action:@selector(takePicAction) forControlEvents:UIControlEventTouchUpInside];
_takePicButton.tag = MOREVIEW_BUTTON_TAG + 2;
_maxIndex = 2;
[_scrollview addSubview:_takePicButton];

CGRect frame = self.frame;
if (type == EMChatToolbarTypeChat) {
frame.size.height = 150;
_audioCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_audioCallButton setTitle:@"语音" forState:UIControlStateNormal];//改动
[_audioCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_audioCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_audioCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_audioCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_audioCallButton setFrame:CGRectMake(insets * 4 + CHAT_BUTTON_SIZE * 3, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCall"] forState:UIControlStateNormal];
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCallSelected"] forState:UIControlStateHighlighted];
[_audioCallButton addTarget:self action:@selector(takeAudioCallAction) forControlEvents:UIControlEventTouchUpInside];
_audioCallButton.tag = MOREVIEW_BUTTON_TAG + 3;
[_scrollview addSubview:_audioCallButton];

_videoCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_videoCallButton setTitle:@"视频" forState:UIControlStateNormal];//改动
[_videoCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_videoCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_videoCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_videoCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_videoCallButton setFrame:CGRectMake(insets, 10 * 2 + CHAT_BUTTON_SIZE + 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCall"] forState:UIControlStateNormal];
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCallSelected"] forState:UIControlStateHighlighted];
[_videoCallButton addTarget:self action:@selector(takeVideoCallAction) forControlEvents:UIControlEventTouchUpInside];
_videoCallButton.tag =MOREVIEW_BUTTON_TAG + 4;
_maxIndex = 4;
[_scrollview addSubview:_videoCallButton];
}
else if (type == EMChatToolbarTypeGroup)
{
frame.size.height = 80;
}
self.frame = frame;
_scrollview.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_pageControl.frame = CGRectMake(0, CGRectGetHeight(frame) - 20, CGRectGetWidth(frame), 20);
_pageControl.hidden = _pageControl.numberOfPages<=1;
}




本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂

qrcode_for_gh_bc92a063b4a2_430.jpg

 
项目完整源码
0
评论

【环信征文】|两处小改动,解决环信V3.0官方版本关于转发的bug 转发 即时通讯 环信 IM

cokeyer 发表了文章 • 202 次浏览 • 2017-08-02 00:57 • 来自相关话题

    (本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢)
    
    最近接手了一个集成即时通讯功能的项目,用的是环信的SDK。用环信的接口可以快速实现即时通讯的很多功能。并且对官方demo稍加改动基本能够满足项目需求。真机测试时,发现图片的转发,每次都是转发失败。我开始以为是我集成时有疏漏,逐行检查代码。发现并不是我的问题。从app store下载的官方demo同样是转发失败!!坑我啊!!原因是ContactListSelectViewController这个控制器里无法正确获取到想转发的图片的缓存地址。
修改如下图:





ContactListSelectViewController.m
代码拷走直接用- (BOOL)messageViewController:(EaseMessageViewController *)viewController
didLongPressRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [self.dataArray objectAtIndex:indexPath.row];
if (![object isKindOfClass:[NSString class]]) {
EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
//////////////////////////解决转发问题的代码///////////////////////////////
EMImageMessageBody *imageBody = (EMImageMessageBody*)[cell.model.message body];
EMMessageBodyType ty = cell.model.bodyType;
if (ty == EMMessageBodyTypeImage) {
NSString *str = cell.model.message == nil ? cell.model.thumbnailFileURLPath : [imageBody localPath];
[[NSUserDefaults standardUserDefaults] setValue:str forKey:@"imgTosand"];
}
/////////////////////////解决转发问题的代码////////////////////////
[cell becomeFirstResponder];
self.menuIndexPath = indexPath;
[self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
}
return YES;
}ContactListSelectViewController是取数据,那么存数据要在ChatViewController控制器做存数据的操作。消息类型写死为EMChatTypeChat,是因为,不论是从单聊界面转发,还是从群聊界面转发,都只能转发给个人,所以这里写死,目前没有问题。
如下图:





ChatViewController.m
代码拷走直接用#pragma mark - EMUserListViewControllerDelegate
- (void)userListViewController:(EaseUsersListViewController *)userListViewController
didSelectUserModel:(id<IUserModel>)userModel
{
if (!self.messageModel) {
return;
}
if (self.messageModel.bodyType == EMMessageBodyTypeText) {
EMMessage *message = [EaseSDKHelper sendTextMessage:self.messageModel.text to:userModel.buddy messageType:EMChatTypeChat messageExt:self.messageModel.message.ext];
__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
// NSMutableArray *array = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
UIViewController *chatController = nil;
#ifdef REDPACKET_AVALABLE
chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
chatController = [[ChatViewController alloc]
initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? [userModel.nickname copy] : [userModel.buddy copy];
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakself.navigationController setViewControllers:array animated:YES];
[weakself.navigationController popViewControllerAnimated:YES];
} else {
[self showHudInView:self.view hint:Localized(@"transpondFail")];
}
}];
} else if (self.messageModel.bodyType == EMMessageBodyTypeImage) {
[self showHudInView:self.view hint:Localized(@"transponding")];
__weak typeof(self) weakSelf = self;
NSString *localPath = [(EMImageMessageBody *)self.messageModel.message.body thumbnailLocalPath];
//////////////////////////解决转发问题的代码////////////////////////////
localPath = [[NSUserDefaults standardUserDefaults] valueForKey:@"imgTosand"];
//////////////////////////解决转发问题的代码//////////////////////////
UIImage *image = [UIImage imageWithContentsOfFile:localPath];
void (^block)() = ^(EMMessage *message){
EMImageMessageBody *imgBody = (EMImageMessageBody *)message.body;
NSString *from = [[EMClient sharedClient] currentUsername];
EMImageMessageBody *newBody = [[EMImageMessageBody alloc] initWithData:nil thumbnailData:[NSData dataWithContentsOfFile:imgBody.thumbnailLocalPath]];
newBody.thumbnailLocalPath = imgBody.thumbnailLocalPath;
newBody.thumbnailRemotePath = imgBody.thumbnailRemotePath;
newBody.remotePath = imgBody.remotePath;
EMMessage *newMsg = [[EMMessage alloc] initWithConversationID:userModel.buddy from:from to:userModel.buddy body:newBody ext:message.ext];
// newMsg.chatType = message.chatType;//此为环信代码
newMsg.chatType = EMChatTypeChat;//这里是我加的
[[EMClient sharedClient].chatManager sendMessage:newMsg progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}
[(EMImageMessageBody *)message.body setLocalPath:imgBody.localPath];
[[EMClient sharedClient].chatManager updateMessage:message completion:nil];

// NSMutableArray *array = [NSMutableArray arrayWithArray:[weakSelf.navigationController viewControllers]];

#ifdef REDPACKET_AVALABLE
RedPacketChatViewController *chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
ChatViewController *chatController = [[ChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? userModel.nickname : userModel.buddy;
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakSelf.navigationController setViewControllers:array animated:YES];
[weakSelf.navigationController popViewControllerAnimated:YES];//转发完跳回去
}];
};

if (!image) {
[[EMClient sharedClient].chatManager downloadMessageThumbnail:self.messageModel.message progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}

block(message);
}];
} else {
block(self.messageModel.message);
}
}
}上面一定要判断一下消息体类型,只有消息体为图片类型(EMMessageBodyTypeImage)才需要保存图片本地。如果不做判断的话,点击气泡马上崩掉。
个人感觉虽然能解决图片转发的问题,但并不是最好的解决办法,虽然对环信demo的代码改动最少。有更好的办法,欢迎在评论区交流。
(本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢) 查看全部
    (本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢)
    
    最近接手了一个集成即时通讯功能的项目,用的是环信的SDK。用环信的接口可以快速实现即时通讯的很多功能。并且对官方demo稍加改动基本能够满足项目需求。真机测试时,发现图片的转发,每次都是转发失败。我开始以为是我集成时有疏漏,逐行检查代码。发现并不是我的问题。从app store下载的官方demo同样是转发失败!!坑我啊!!原因是ContactListSelectViewController这个控制器里无法正确获取到想转发的图片的缓存地址。
修改如下图:

2893691-7bfd41d8c42b5f3d.png

ContactListSelectViewController.m
代码拷走直接用
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
didLongPressRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [self.dataArray objectAtIndex:indexPath.row];
if (![object isKindOfClass:[NSString class]]) {
EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
//////////////////////////解决转发问题的代码///////////////////////////////
EMImageMessageBody *imageBody = (EMImageMessageBody*)[cell.model.message body];
EMMessageBodyType ty = cell.model.bodyType;
if (ty == EMMessageBodyTypeImage) {
NSString *str = cell.model.message == nil ? cell.model.thumbnailFileURLPath : [imageBody localPath];
[[NSUserDefaults standardUserDefaults] setValue:str forKey:@"imgTosand"];
}
/////////////////////////解决转发问题的代码////////////////////////
[cell becomeFirstResponder];
self.menuIndexPath = indexPath;
[self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
}
return YES;
}
ContactListSelectViewController是取数据,那么存数据要在ChatViewController控制器做存数据的操作。消息类型写死为EMChatTypeChat,是因为,不论是从单聊界面转发,还是从群聊界面转发,都只能转发给个人,所以这里写死,目前没有问题。
如下图:

2893691-7beb949a62e8035f.png

ChatViewController.m
代码拷走直接用
#pragma mark - EMUserListViewControllerDelegate
- (void)userListViewController:(EaseUsersListViewController *)userListViewController
didSelectUserModel:(id<IUserModel>)userModel
{
if (!self.messageModel) {
return;
}
if (self.messageModel.bodyType == EMMessageBodyTypeText) {
EMMessage *message = [EaseSDKHelper sendTextMessage:self.messageModel.text to:userModel.buddy messageType:EMChatTypeChat messageExt:self.messageModel.message.ext];
__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
// NSMutableArray *array = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
UIViewController *chatController = nil;
#ifdef REDPACKET_AVALABLE
chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
chatController = [[ChatViewController alloc]
initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? [userModel.nickname copy] : [userModel.buddy copy];
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakself.navigationController setViewControllers:array animated:YES];
[weakself.navigationController popViewControllerAnimated:YES];
} else {
[self showHudInView:self.view hint:Localized(@"transpondFail")];
}
}];
} else if (self.messageModel.bodyType == EMMessageBodyTypeImage) {
[self showHudInView:self.view hint:Localized(@"transponding")];
__weak typeof(self) weakSelf = self;
NSString *localPath = [(EMImageMessageBody *)self.messageModel.message.body thumbnailLocalPath];
//////////////////////////解决转发问题的代码////////////////////////////
localPath = [[NSUserDefaults standardUserDefaults] valueForKey:@"imgTosand"];
//////////////////////////解决转发问题的代码//////////////////////////
UIImage *image = [UIImage imageWithContentsOfFile:localPath];
void (^block)() = ^(EMMessage *message){
EMImageMessageBody *imgBody = (EMImageMessageBody *)message.body;
NSString *from = [[EMClient sharedClient] currentUsername];
EMImageMessageBody *newBody = [[EMImageMessageBody alloc] initWithData:nil thumbnailData:[NSData dataWithContentsOfFile:imgBody.thumbnailLocalPath]];
newBody.thumbnailLocalPath = imgBody.thumbnailLocalPath;
newBody.thumbnailRemotePath = imgBody.thumbnailRemotePath;
newBody.remotePath = imgBody.remotePath;
EMMessage *newMsg = [[EMMessage alloc] initWithConversationID:userModel.buddy from:from to:userModel.buddy body:newBody ext:message.ext];
// newMsg.chatType = message.chatType;//此为环信代码
newMsg.chatType = EMChatTypeChat;//这里是我加的
[[EMClient sharedClient].chatManager sendMessage:newMsg progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}
[(EMImageMessageBody *)message.body setLocalPath:imgBody.localPath];
[[EMClient sharedClient].chatManager updateMessage:message completion:nil];

// NSMutableArray *array = [NSMutableArray arrayWithArray:[weakSelf.navigationController viewControllers]];

#ifdef REDPACKET_AVALABLE
RedPacketChatViewController *chatController = [[RedPacketChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#else
ChatViewController *chatController = [[ChatViewController alloc] initWithConversationChatter:userModel.buddy conversationType:EMConversationTypeChat];
#endif
chatController.title = userModel.nickname.length != 0 ? userModel.nickname : userModel.buddy;
// if ([array count] >= 3) {
// [array removeLastObject];
// [array removeLastObject];
// }
// [array addObject:chatController];
// [weakSelf.navigationController setViewControllers:array animated:YES];
[weakSelf.navigationController popViewControllerAnimated:YES];//转发完跳回去
}];
};

if (!image) {
[[EMClient sharedClient].chatManager downloadMessageThumbnail:self.messageModel.message progress:nil completion:^(EMMessage *message, EMError *error) {
if (error) {
[weakSelf showHudInView:self.view hint:Localized(@"transpondFail")];
[weakSelf performSelector:@selector(backAction) withObject:nil afterDelay:1];
return ;
}

block(message);
}];
} else {
block(self.messageModel.message);
}
}
}
上面一定要判断一下消息体类型,只有消息体为图片类型(EMMessageBodyTypeImage)才需要保存图片本地。如果不做判断的话,点击气泡马上崩掉。
个人感觉虽然能解决图片转发的问题,但并不是最好的解决办法,虽然对环信demo的代码改动最少。有更好的办法,欢迎在评论区交流。
(本人github:https://github.com/BHAreslee)(若转载,请告知本人并附上原文链接,谢谢)