注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

Demo体验

Demo体验

场景Demo,开箱即用
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

环信:基于IM的移动端全媒体智能客服

        环信是一家SAAS公司,我们以SAAS形式向企业提供客户服务软件。 当用户处于移动互联网时代,用户都去手机上了,在手机上提供什么样的客户服务软件呢。我们认为在手机上的客户服务软件只有一种形式,就是我们以前用的那种IM技术,就是说你...
继续阅读 »
 


 
 
 
环信是一家SAAS公司,我们以SAAS形式向企业提供客户服务软件。


94716214.png


当用户处于移动互联网时代,用户都去手机上了,在手机上提供什么样的客户服务软件呢。我们认为在手机上的客户服务软件只有一种形式,就是我们以前用的那种IM技术,就是说你用一个很自然的人机沟通得方法,你跟你的客服聊天,问他各种问题。这是一个在手机上唯一一个可行也是经过,微信陌陌这种大量验证过的可用的客户服务形式。


34105317.png


我们是从做产品出身,过渡到做移动APP 的客服。现在来说,我会把这个所谓云客服的市场分为4块。第一是APP的客服,第二块是社交媒体,主要是微信和微博,第三块是网页在线客服,第四块是呼叫中心,那我们是在APP的内置客服这块做的最好的。那我们有环信技术通讯云的技术优势,这个优势领先其他公司非常大。


84601450.png


大家很多人可能都在网页上买过东西,问过客服,那么其实会有这么一个问题,你把网页一关,除非你给客服留下电话号码QQ号,否则客服找不到你的,因为网页已经关掉了,聊天窗口关掉了。所以现在我们有一种技术,我可以告诉你说,只要消费者不卸载你的APP,哪怕把你的APP杀死了,没关系,任何时候我能让客服坐席反过来跟消费者说话。因为我们属于IM长连接技术,这个技术是保证了在你的手机APP和客服的服务器有一个永远的连接,哪怕你这个APP到后台这个连接也活着,这时候永远都能反过来找消费者,这个技术是一个非常巨大的进步。


56526187.jpg


我们后台做了很多工作,比如说大家有一个共享的快捷回复,只要问这个问题,你点回复就直接回复了。我们会发现其实在这种聊天客服过程中,很多问题都重复的,那么这种问题我们是有固定答案的,这种情况下不需要过真人,先过滤到我们的机器人,百分之六十到八十的问题机器人就替你回答了,当机器人回答不了这个问题的时候,这个问题才会从机器人这条线,进到第二线,进到人工这条线,人工开始回答。人工在回答的时候也有很多工具帮助你,比如说,消费者每说一句话,这句话都会进到机器人知识库,那知识库就会对这个问题做匹配,它就会对这句话做一些建议的回答。客服本来要打20个字,现在变成点一些鼠标,就发出去了,所以我们有很多这样的小工具,一些人工智能的技术,来帮助咱们客服来提高工作效率。


46958456.png


我们赚钱很简单,我们就是按照席位卖钱。比如说我有一个大用户,他在我这买了大概三万个席位,我们一个席位是1500一年的,他一算三万乘以一千五,应该付我多少钱付多少钱。

我们团队现在人也比较多,现在一百五十多个人,我们是一个特别技术驱动型的团队。在我看来,做一个创业公司,做一个SAAS公司,销售很重要,市场也很重要,但是其实我觉得最重要的还是产品。我们在过去几年看到了兴起的很多创业公司,他们是以强运营,以商业模式为主的创业公司,但是我们判断从去年开始到后面这一年,会出现一批新的创业公司,就是以技术为导向型的。 收起阅读 »

表情mm|表情云™全面兼容环信SDK,打造更完整的富媒体消息解决方案

  2016年1月20日起,国内首家表情云服务平台——表情mm|表情云™将全面兼容环信IM SDK,所有接入环信IM服务的产品都可以下载表情mm环信版SDK,快速接入表情云服务,轻松拥有自己的表情商店。   接入效果演示——轻元素APP 轻元素是...
继续阅读 »
 
2016年1月20日起,国内首家表情云服务平台——表情mm|表情云™将全面兼容环信IM SDK,所有接入环信IM服务的产品都可以下载表情mm环信版SDK,快速接入表情云服务,轻松拥有自己的表情商店。


表情mm完美兼容环信2.png


 
接入效果演示——轻元素APP


轻元素是国内首个运动减肥打卡社交平台,上线之初就获得小米应用市场精品推荐,目前Android/iOS线上版均已接入环信IM SDK和表情mm SDK。


轻元素演示效果拼图.jpg





 小表情图文混排,大表情一键发送
 


为了适应用户习惯,表情mm|表情云™实现了“小表情图文混排,大表情一键发送”的功能,使用体验上向手Q和微信靠拢。


小表情图文混排.jpg





表情mm|表情云™五大特点


1、    国内最专业的表情SDK
灵活的架构和开放层次,所有图片托管于CDN加速网络 ,是国内最稳定快速的表情云平台
优秀的内容接入体系,IP版权方,第三方表情作者都可以方便的参与内容贡献
易于扩展,易于定制,可以根据自身需求灵活组装与定制。同时提供默认的表情键盘与表情商店界面,真正实现几行代码,一分钟接入

2、    最接地气的表情商店
与同道大叔、搜狐视频、魔鬼猫、咸蛋超人等深受90后用户喜爱的IP进行深度订制合作
拥有四年表情制作经验,对市场理解深入

3、    高品质·高逼格
微信·陌陌·搜狗输入法·华谊兄弟等巨头的合作伙伴,绘制包括巴萨球星系列·坏猴子·私人订制·有一天·小王子在内的正版官方表情
强大的IP资源,远离繁杂的授权纷争
高端•高质•高辨识度

4、    一站式解决表情问题
专门团队追随网络热点,超快企划超快更新
与多家大型影视公司、视频网站达成深度合作,对重点企划抢先了解、抢先制作
节约宝贵的开发时间,省去昂贵的外包费用,没有更新不及时的后顾之忧,开发者所有的表情问题,都可以得到一站式的解决

5、    整合数据服务
通过表情数据,了解用户对表情、动漫、明星的喜好,能为公司的界面风格、官方形象、活动设计、礼品包装、明星合作、推广阵地、增值服务、周边设计等方面提供有价值的参考
除了文字和语音,表情一直是即时通讯中重要的一部分,表情mm|表情云™和环信即时通讯云,此次强强联合,将为开发者提供更全面的即时通讯服务,更有效的富媒体消息解决方案,进一步提升用户体验,增强产品在市场上的竞争力。
 


下载地址:点击下载 收起阅读 »

不同技能的测试工程师是如何正确的进行自动化测试

  很多人理解的自动化就是把手工测试case用脚本和工具转变成app自动化测试。也就是说把手工测试的每一个步骤用脚本来模拟,从而执行testcase。那么自动化的所有问题就归结于,如何用工具和脚本来转化手工操作步骤了。还有很多非常senior的,但是不会cod...
继续阅读 »

 
很多人理解的自动化就是把手工测试case用脚本和工具转变成app自动化测试。也就是说把手工测试的每一个步骤用脚本来模拟,从而执行testcase。那么自动化的所有问题就归结于,如何用工具和脚本来转化手工操作步骤了。还有很多非常senior的,但是不会coding的手工测试工程师强调case的design能力是如何如何重要,自动化相对来说不是那么重要。

我这里可以肯定的说,没有好的编程功底,你也不可能设计出非常好的testcase,自动化的开发也不应该是仅仅把手工操作用脚本来模拟,而是应该大幅度的改变testcase,使得能够用最好的方式来进行自动化。那些手工测试人员所谓的设计case的重要性,和他们设计case的高水平,实际上只是在他们的知识范围之内产生的观点。下边我用一个小例子来说明,编程能力在自动化过程中起的作用到底有多大。基本上来讲,有多强的开发水平,就有多强的自动化设计,实现水平。自动化开发和产品的开发实际上都是一样的,都是有需求,你来实现。当然,不同水平的人,实现起来的效果是千差万别的。这也就是为什么开发有高手,有低手,自动化测试的开发也同样有低手,有高手。自动化测试水平没有上限,你要学会发挥自己的无穷潜力。

不多说了,现在说一下我们要自动化什么问题。我们有两个计算机帐号,A和B。我们需要用B帐号进行系统的设置,也就是测试的准备工作,然后用A帐号来进行测试。下边来说一下不同水平的人是如何进行自动化的。

1. 手工测试人员

 

 

Log on B

Configure

Log out

Log on A

Test

 

 

2. 初级自动化人员(直接把手工case转成自动化)

 

 

Set autologon B

Set autorun

Record test status: 0

Logout

Check status

if(status==0)

{

Configure

Set autologon A

Record test status:1

Logout

}

if(status==1)

{

Test

}

 

 

这个级别的人,需要懂得脚本编程,需要懂得系统设置,autologon and autorun。

3. 有一定经验的自动化人员(改变手工测试case以利于自动化的更简单,可靠的实现)

不需要log out and log on


利用Windows命令Runas

用高级语言调用Runas

利用重定向来输入Password

这个级别的人,需要懂得高级语言,重定向,Windows系统命令Runas

4. 中级自动化人员(具有更丰富的开发经验,可以用程序代替UI和系统命令)

不需要Runas命令

利用.NET的Process对象

用B的身份生成一个Process来进行配置工作

这个级别的人,要比较熟悉高级语言,比较熟悉高级语言的类库,懂得操作系统的内核基本概念

5. 高级自动化人员(精通高级语言,精通操作系统内核)

不需要多生成一个进程

用本线程impersonate用户B

利用.NET WindowsIdentity 对象

必须要调用Windows API,LogonUser

这个级别的人,要精通C/C++和Java,C#等高级语言,精通Windows内核的知识和Windows API

从以上的例子可以看到,针对同一个testcase,不同的测试人员,从手工到高级自动化,由于自己知识面的原因,会设计出非常不同的case出来。越高级的自动化越灵活,稳定,可靠,也更需要掌握更多的开发和内核的知识。因此,我们看到很多人在强烈的否定自动化,你先看看他到底在哪个层次中。越下边层次的自动化人员,由于技术的原因,碰到的问题会越多,能解决的问题却越少,因此对自动化的抱怨也就越大了。这些都是可以理解的,不过以此来否定自动化,我觉得还是不太应该,毕竟自己技术还不过关。

  收起阅读 »

【原来配置环信apns就这么简单】内含各种问题点详讲

  配置环信apns推送消息的准备工作配置证书: 链接1:http://docs.easemob.com/doku.php?id=start:300iosclientintegration:10prepareforsdkimport  工程中需要写的代码: ...
继续阅读 »
 
配置环信apns推送消息的准备工作配置证书:
链接1:http://docs.easemob.com/doku.php?id=start:300iosclientintegration:10prepareforsdkimport 

工程中需要写的代码:
链接2:http://docs.easemob.com/doku.php?id=start:300iosclientintegration:80apns


如果按照以上方法配置完以后,测试的时候,如果还是收不到apns推送消息的话,按照下面步骤进行排查。注 意:(app在后台静默3分钟以上或者杀掉app,长连接断开才会走apns推送,3分钟以内的话要想收到消息通知,需要实现本地通知,环信demo是实 现本地通知的方法在 MainViewController.m类  - (void)showNotificationWithMessage:(EMMessage *)message,这个方法是在接收消息的回调中被调用的。具体的请查看demo。还需要注意一点的是,看看自己是否设置了全局免打扰,就是说在某个时段不接收apns推送消息,一般新集成的是不会设置的,设置代码在上面第二个链接2中)另外还要注意的是请确保导出p12时使用的电脑和创建 CertificateSigningRequest.certSigningRequest文件的电脑是同一台,导出证书的时候要直接点击导出,不要点击下面的内容导出,确认申请的证书是否带有推送功能。

1.检查下你后台绑定的证书名称和你工程里面的名称是不是对应的 ,初始化appkey的方法 填写的证书名称 (如图)


1.png



2.看下devicetoken是否传给了SDK,然后在环信管理后台看下IM用户是否显示了证书名称,如果显示了,说明devicetoken传给SDK,绑定成功了。
// 将得到的deviceToken传给SDK (真机上获取,打印下deviceToken)
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    [[EaseMob sharedInstance] application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
} (如图)

3.png




2.png



3.调用rest接口,查看下登录的用户,绑定的证书名称和devicetoken是不是正确。这里说下查看的方法。
  1)首先看下这个链接http://docs.easemob.com/doku.p ... users
 调用rest接口,需要你的appkey ,Client Id,Client Secret(到环信管理后台查看),获取  到token
 2)例子:
 curl -X POST
"https://a1.easemob.com/easemob-demo/chatdemoui/token"  '{"grant_type":"client_credentials","client_id":"YXA6wDs-  MARqEeSO0VcBzaqg11","client_secret":"YXA6JOMWlLap_YbI_ucz77j-4-mI0dd"}'
 这 个是环信的,改成自己的话,将“easemob-demo/chatdemoui” 换成自己appkey #号前后两  部分,"client_id"和"client_secret"后面的参数换成自己的Client Id与Client Secret对应  的参数,替换完成之后,复制粘贴到终端上,点击回车运行,如果没有出错会获取  到"access_token"后面的参数。
 3)然后调用链接里面的 给IM用户的添加好友的接口
 例 子:curl -X POST -H "Authorization: Bearer YWMtP_8IisA-EeK-  a5cNq4Jt3QAAAT7fI10IbPuKdRxUTjA9CNiZMnQIgk0LEU2"
'https://a1.easemob.com/easemob-demo/chatdemoui/users/jliu/contacts/users/yantao'
 YWMtP_8IisA- EeK-a5cNq4Jt3QAAAT7fI10IbPuKdRxUTjA9CNiZMnQIgk0LEU2这个参数换成自己获  取到的"access_token"后面的参数,‘easemob-demo/chatdemoui’换成自己的  appkey,‘jliu’和 ‘yantao’替换成自己的环信 ID,‘yantao’这个环信ID一定要是登录状  态的,只有在登录状态才会获取到他绑定的证书名称和devicetoken。替换完成之后,复制粘贴  到 终端上,点击回车,正确的话,会从得到的信息中看到"notifier_name"和"device_token"这  两个参数,就是证书名称和 devicetoken,检查下是否正确。

4.测试推送证书的时候,首先登录两个环信ID(其中一个账号需要真机登录),相互收发消息,如果没有 问题的话,那么将真机上的app直接杀掉(双击 home键,找到对应的app杀掉),然后给之前真机登录的环信ID发消息,如果一切都正常的话,那么会收到apns推送消息,通知栏会有提示。

5. 如果还是收不到推送的话,可以将p12证书,证书密码,devicetoken,还有是什么环境的证书,这些信息发给环信的技术支持,帮着测试下证书。一般收 不到推送,都是证书的问题,需要重新配置。(如果是生产环境的证书,需要你的app上传到AppStore或者ad hoc打包,才能测试。)


收起阅读 »

集成环信3.0 处理UI上展示昵称 头像的方法

 因为遇到了不少用户问到了这个问题,所以在这里总结一下。 首先明确一下,环信只是即时通讯的消息引擎。环信本身不提供用户体系,环信既不保存任何APP业务数据,也不保存任何APP的用户信息。 根据环信ID来绑定用户的昵称,头像,方便维护。 环信3.0 demo...
继续阅读 »
 因为遇到了不少用户问到了这个问题,所以在这里总结一下。

首先明确一下,环信只是即时通讯的消息引擎。环信本身不提供用户体系,环信既不保存任何APP业务数据,也不保存任何APP的用户信息。

根据环信ID来绑定用户的昵称,头像,方便维护。 环信3.0 demo中,是用parse来管理昵称,头像的(parse是管理昵称,头像的一个三方库,将头像,昵称上传到parse服务器,在从parse服务器获取),从自己服务器获取的话就按照下面的方法参考一下吧。


1.  服务器维护昵称,头像的方案先看下这个链接: http://docs.easemob.com/im/490integrationcases/10nickname

2. 从自己服务器获取到用户的昵称,头像后,会话列表类的替换,在EaseConversationCell.m类,- (void)setModel:(id<IConversationModel>)model中              [self.avatarView.imageView sd_setImageWithURL:[NSURL URLWithString:_model.avatarURLPath] placeholderImage:_model.avatarImage];   这个方法就是来展示头像的

3. 聊天类的替换,在EaseBaseMessageCell.m类,- (void)setModel:(id<IMessageModel>)model中      
if (model.isSender) {
        UIImage *placeholderImage = [UIImage imageNamed:@"123"];
        self.avatarView.image = placeholderImage;
    } else {
        if (model.avatarURLPath) {
            [self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage];
        } else {
            self.avatarView.image = model.avatarImage;
        }
    }
if (model.isSender) 我自己加的判断 区分发送者和接受者的头像(isSender判断是不是当前登录者),如果想在这个类中想要获取到对方的环信ID,那么引入 #import <EMMessage.h>头文件,    EMMessage *message = model.message;   NSString *username = message.from;就可以获取到了,然后自己在根据环信ID自己做处理,展示。  如果是群聊的话,想要获取到群成员在群里发送消息人的环信ID,通过message.groupSenderName 获取到。

4.联系人类的替换,在EaseUserCell.m类,- (void)setModel:(id<IUserModel>)model中,self.titleLabel.text = _model.buddy.username;          [self.avatarView.imageView sd_setImageWithURL:[NSURL URLWithString:_model.avatarURLPath] placeholderImage:_model.avatarImage];  


 
具体的到3.0demo中 自己看一下吧, 仅仅给提供个参考。 收起阅读 »

年底了,你们公司搞年会了吗?年会节目都是啥样的呢?

 2015年过去了,很早就准备写这篇稿子,最近一直忙碌到现在才整理出来。   这一年,时间就在不经意间悄然逝去。   一年的时间,改变来的太快。每每辞旧迎新的时刻,互联网人依然停不下来忙碌加班的脚步(心疼我自己)~~ 一年的时间,产品更新迭代。互联网行业的...
继续阅读 »
 2015年过去了,很早就准备写这篇稿子,最近一直忙碌到现在才整理出来。
 
这一年,时间就在不经意间悄然逝去。
 


一年的时间,改变来的太快。每每辞旧迎新的时刻,互联网人依然停不下来忙碌加班的脚步(心疼我自己)~~

一年的时间,产品更新迭代。互联网行业的迷人之处就在于快速成长,
我们终究是收获了!

一年的时间,业务有好有坏。但因为有小伙伴们互相的宽容与支持,我们终究是在这个“互联网寒冬”中挺过来了。


 在这里,环信预祝各位小伙伴们在新的一年蓬勃发展,日胜一日。
 
昨天接到通知,今年年会在本月下旬的时候吃饭+年会,每个部门出1-2个节目娱乐一下,放假前搞一个晚会。
又到了提节目的时候,去年可是为了年会节目几度失眠呀!
 


感觉唱歌太单调了,跳舞不会,准备点啥节目好呢?大家年会是怎么过的啊? 

年会到了,不妨我们停下脚步,好好享受这难得的恬静时光。


 
这里上传几张去年环信年会照片给亲们感受下,向过去致敬!
 



mmexport1452674311325.jpg




mmexport1452674406870.jpg




mmexport1452674320951.jpg



 
 年底了,你们公司有搞年会吗?节目有哪些?有抽到什么礼物呢? 收起阅读 »

[huanxin-sdk] 环信 Rest Api Node sdk

  The node sdk of Huanxin Rest API for high performance 详细用法请移步:test/,建议使用 redis 存储token 'use strict'; var Huanxin = require('hu...
继续阅读 »
 
The node sdk of Huanxin Rest API for high performance


详细用法请移步:test/,建议使用 redis 存储token

'use strict'; var Huanxin = require('huanxin-sdk'); 
var huanxin = new Huanxin({ 
org_name : 'your_org_name',
 app_name : 'your_app_name',
 client_id : 'your_client_id',
 client_secret : 'your_client_secret',
 tokenSet: function(err, data){
 /**
 * data = { 
 * access_token: '环信返回的token值',
 * expires_in: '过期时间(秒),按照当前返回是60天,但是实际是7天就会过期,不可用',
 * application: '应用id'
 * } 
*/ 
// 518400 设置6天过期 
redisClient.setex('HXTOKEN_TEST1', 518400, data.access_token, function(err, res){ DEBUG_HUAXIN('TokenSet err %j, res %s', err, res);
 });
 },
 tokenGet: function(callback){
 // token 缓存层,内部不会调用 getToken 方法实时获取
 /**
 * @return {String} token 
 */ 
 redisClient.get('HXTOKEN_TEST1', callback);
 } 
});
 huanxin.getToken(function(err, data){ 
//...something to do ... 
});
 


项目下载:


NPM:huanxin-sdk
GitHub:huanxin-sdk


 
  收起阅读 »

服务端调用rest接口示例demo

  服务端怎么集成? --写http请求调rest接口,对语言没限制,不需要导jar   有没有觉得这句回复很熟悉?   看到经常有服务端、后台 集成不知道怎么弄的,这里就把环信服务器端API的示例代码整理出来供大家参考 下载地址:https://git...
继续阅读 »
 


服务端怎么集成?
--写http请求调rest接口,对语言没限制,不需要导jar


 
有没有觉得这句回复很熟悉?
 
看到经常有服务端、后台 集成不知道怎么弄的,这里就把环信服务器端API的示例代码整理出来供大家参考


下载地址:https://github.com/easemob/emchat-server-examples


 
大家在使用过程中有什么问题 建议欢迎直接提出。
 
这里收录几个社区贡献 的demo ,大家有自己写的也可以联系我放在这里展示!


Java实现环信服务器端接口:http://www.imgeek.org/article/825307464

php和nodejs服务端代码示例参考:http://www.imgeek.org/article/825307461

关于环信.net服务端demo的一点补充(解决出错无法返回json格式的问题):http://www.imgeek.org/article/825307540
 
 Node.js调用rest接口http://www.imgeek.org/article/825307544
 
.net调用rest接口http://www.imgeek.org/article/825308176


收起阅读 »

关于环信.net服务端demo的一点补充(解决出错无法返回json格式的问题)

前段时间做基于.net的环信服务端开发 。 .net的demo链接地址如下: https://github.com/easemob/emchat-server-examples/blob/master/emchat-server-dotnet/EaseMobD...
继续阅读 »
前段时间做基于.net的环信服务端开发 。
.net的demo链接地址如下:
https://github.com/easemob/emchat-server-examples/blob/master/emchat-server-dotnet/EaseMobDemo.cs
发现示例demo有一个比较关键的问题没有解决,
就是EaseMobDemo的public   string   ReqUrl(string reqUrl, string method, string paramData, string token)
这个函数,如果stream流读取resp发生错误的话,无法返回json类型的字符串,而是直接400,500之类的错误,
结果无法获得json字符串格式。
 
例如:注册用户时,如果用户已经存在,按照原来的代码
       会返回 {"远程服务器返回错误: (400) 错误的请求。"}
       这个不是json格式,无法用程序反序列化。
       后来发现Java的服务端demo,如果出错是可以返回json格式的错误代码。
       向环信客服咨询,也没人知道怎么做。
 
      经过研究把代码做了一下修改,前面代码不变,只是把错误捕捉的部分作了修改,增加了一个函数
 
      public   string   ReqUrl(string reqUrl, string method, string paramData, string token)
        {
            try
            {
                HttpWebRequest  request = WebRequest.Create(reqUrl) as HttpWebRequest;
                request.Method = method.ToUpperInvariant();
                request.ContentType = "application/json";

                if (!string.IsNullOrEmpty(token) && token.Length > 1)
                {
                    request.Headers.Add("Authorization", "Bearer" +" "+ token);
                }
     
                if (request.Method.ToString().Trim() != "GET"  &&  !string.IsNullOrEmpty(paramData)  &&  paramData.Length > 0)
                {
                    byte buffer = Encoding.UTF8.GetBytes(paramData);
                    request.ContentLength = buffer.Length;
                    request.GetRequestStream().Write(buffer, 0, buffer.Length);
                }

                 using (HttpWebResponse resp = request.GetResponse() as HttpWebResponse)
                 {
                     using (StreamReader stream = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
                     {
                        return stream.ReadToEnd();
                    }
                }

            }
            catch (WebException e)
            {
                return GetErrorInfo(e);
            }

        }

        ///
        /// 增加此方法,用户可以返回json格式错误。
        ///

        ///
        ///
        private  string  GetErrorInfo(WebException ex)
        {
            string  errorinfo = "";
            HttpWebResponse  resp = (HttpWebResponse)ex.Response;
             if (ex.Status == WebExceptionStatus.ProtocolError)
            {
                using (StreamReader streamreader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
                {
                        errorinfo = streamreader.ReadToEnd();
                 }
            }
               return   errorinfo;
        }
 
这样就是发生错误,也返回json格式。
以下是注册已经存在的用户,错误返回格式
{
    "error":"duplicate_unique_property_exists",
    "timestamp":1452661004439,
    "duration":0,
    "exception":"org.apache.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException",
    "error_description":"Application fbd110b0-884d-11e5-8f9c-e94cec2e0ad0 Entity user requires that property named  username be unique, value of fan exists"
}
为其他开发者少走弯路,提供一定的参考。 收起阅读 »

环信扩展消息的UI问题

我在自定义一个扩展消息的时候,UI格式参照环信File的类型写的,可是UI却显示不出来,麻烦谁能帮我解决下。代码和截图如下    
我在自定义一个扩展消息的时候,UI格式参照环信File的类型写的,可是UI却显示不出来,麻烦谁能帮我解决下。代码和截图如下
 
 

程序员黑客马拉松等你来战

关于猿圈科技 猿圈(www.oxcoder.com)是一家专业的技术测评网站,通过平台帮助企业高效省时地识别最优秀的编程技术人才。同时帮助程序员提升编程能力,用代码脱颖而出,在获得工作机会的同时,识别程序员在编程过程中的不足,并且提供量身定制的学习板块。   ...
继续阅读 »

GP.png



关于猿圈科技
猿圈(www.oxcoder.com)是一家专业的技术测评网站,通过平台帮助企业高效省时地识别最优秀的编程技术人才。同时帮助程序员提升编程能力,用代码脱颖而出,在获得工作机会的同时,识别程序员在编程过程中的不足,并且提供量身定制的学习板块。
 
关于环信
环信(www.easemob.com)成立于2013年4月,是一家全通讯能力云服务提供商。产品包括全球最大的即时通讯云PaaS平台——环信即时通讯云,以及全球首创的全媒体智能云客服平台——环信移动客服。


关于码易众包
码易众包,致力于提供移动互联网产品设计、开发、运营及推广服务,是一个高质量交付平台,让产品开发更快,让程序员更赚钱。公司目前是国家和中关村高新技术企业,并在北京股权交易中心挂牌成功,入选中关村“金种子工程”企业。


比赛信息

报名要求:   程序猿
活动时间:   2015.12.23-2016.1.23
活动形式:   通过猿圈网站的在线测评平台进行编程挑战;
报名截止:   01月20日(1月20日晚12点报名通道关闭)

选手在本报名页面完成后,会在报名当日晚9点前收到比赛通知邮件,按照邮件内提示即可开始初赛环节。
初赛的前二百名编程高手,届时主办方相关工作人员电话通知,进行线上决赛并角逐出前5名。

比赛奖品

一等奖奖金3000元。二等奖,三等奖奖金若干。
前5名每人将获得一块精美手表。
前5名每人奖励猿圈价值1199元的学习卡一张。
前50名每人将获得麦步科技提供的100元购物券一张。
前200名参与最终的跨年颁奖晚会,将获得知名企业offer,技术大牛经验分享等。

活动现场另有精心礼品相送。


日程安排

本次活动包括线上初赛,线上决赛,跨年颁奖大会三部分,其中初赛,决赛环节,通过猿圈网站的在线测评平台进行编程挑战。
报名初赛: 12月23日----01月20日
线上决赛:  01月10日----01月22日
线下颁奖:  01月23日


黑客马拉松,等你来战!

用你的缜密,用你的创思,用你的明慧改变人生轨迹吧!
让这世界与时代见证你的勤奋,踏实和卓越。
时代召唤着程序员们,名企盼望着技术宅们。
让我听到你们摩拳擦掌想要重出江湖的声音吧!
一语言世界,编程无极限!

立即报名:http://form.mikecrm.com/f.php?t=JU8shq


致谢

感谢深圳市麦步科技有限公司和BeeCloud公司为对本次活动的大力支持!
麦步科技是一家智能穿戴设备行业的创新企业,主要从事智能穿戴类软、硬件的研发与生产,主要产品包括麦步微信计步器、麦步智能手表、麦步APP等。
BeeCloud为开发者提供一站式支付解决方案。通过提供易用友好的支付SDK,几行代码高效实现网页、APP支付功能,并提供可靠稳定可靠的分布式云后端服务,保障支付流程安全流畅。 收起阅读 »

开发一个APP要100万?用互联网思维只要1万

近期经常看到一些关于开发一个App要多少钱的文章出来,有的说App开发要100万,有的说要60万,其实这个不叫开发App,那叫做项目或者创业开公司要多少钱才对,而这种情况下之所以要这么多钱,也是基于一个对行业啥都不懂的情况下来计算的,所有的东西都需要你花钱来完...
继续阅读 »

近期经常看到一些关于开发一个App要多少钱的文章出来,有的说App开发要100万,有的说要60万,其实这个不叫开发App,那叫做项目或者创业开公司要多少钱才对,而这种情况下之所以要这么多钱,也是基于一个对行业啥都不懂的情况下来计算的,所有的东西都需要你花钱来完成的。

我们可以想象一下,你对移动互联网一窍不通,但是你想开个公司,创业做个App,那么还真是啥都要重新招,你需要招聘四个客户端(两个负责ios、两个负责android),两个服务端,一个设计师,一个产品经理,一个人事,一个行政,甚至还需要负责扫地的阿姨……除此之外,还需要注册公司,租办公室,再加上招人又不是一时半会能招到,产品需求一时半晌还确定不了,再加上万一有点程序员太菜,又做不了,加上看错人的失误成本,这样算下来,几百万都打不住。如果你是这样一个创业小白,建议你还是别去创业了,因为这样的创业方法实在太笨了,就是App做出来,创业也会笨死的。

这样的思维方式其实还是传统思维方式,传统思维就是需要什么东西都来自己做,比如生产一个汽车,你要生产轮胎,你需要生产汽车的挡风玻璃,汽车的所有零件你都需要生产,只有这样才能实现你制造汽车的梦想,但是很多人不知道,这样的传统思维早已经过去了,在当今的移动互联网时代,如果还以传统思维来做互联网,那是必定失败的。

而真实的移动互联网是什么?就是你需要生产汽车,但是已经有各个公司已经造出了各种各样的零件,你只要把他们组装在一起就好了。如果用到App开发上来说,就是开发一个App已经很简单了,你只需要把需要App的那些组成部分找个工程师组装起来即可。

下面笔者就给大家来举个例子,说明开发一个App要多少钱,大家可以猜猜,这个App是我认识的一个90后小鲜肉做的,一个校园点餐App,通过这个App大学生可以向学校周围的餐馆订餐,订餐时能看到学校周边每家饭店的特色菜、优惠、价格等等。

下面我就分解下这个App的成本构成,以及开发相关的功能模块。

开发工具:HBuilder 免费

前端:MUI 免费

后端:野狗 免费

统计:友盟SDK 免费

推送:个推 免费

推广渠道:360手机助手 免费

开发周期:3周

开发成本:由于基于mui开发App,生成流应用的同时,iOS、Android原生版、浏览器版、微信App和百度直达号版也随之生成,实现了多端发布,开发成本不到1万块。

开发人数:1人。

……

这个App使用了目前市面比较流行的工具,比如HBuilder;以及集成了很多免费的功能模块,比如友盟的统计、个推的推送。而且这些工具和功能模块都是免费的,所以开发成本是零。另外在开发人员的配置上,由于使用了MUI前端工具,使得一次开发,就完成了ios、和其他终端的生成,减少了开发人员的数量,使得1人就可以完成。所以在跨平台方面和版本适配方面,开发成本也大大降低。这种对移动互联网各种工具的详细了解和熟练使用,使得App的开发成本整体下降很多,最后核算下总的成本仅有1万左右。app测试想省点就自己来,想高效就用testbird

另外,大家可能说这个App很简单,不需要太多的成本,想添加更多的功能,这些都没有问题,在当下的App的开发中有各种各样的功能插件可以让我们免费使用比如融云的IM,可以让你的App具有即时通讯的功能,后端的leancloud可以让你瞬间就有后端的统计、推送等各种功能、还有安全相关的360加固保,可以保护你的App代码,防止被反编译等等,当下的很多2B企业都提供了这些功能模块,在开发App的过程中,我们只要把这些功能模板组装起来即可……这样算下来,我们可以看出,开发一个App其实要不了多少钱。

举这个例子的目的其实就是告诉大家,做开发需要有互联网思维,促进开发资源的合理使用,这样才能降低开发成本。现在的很多App动不动就上百兆非常大,装的多了,手机卡的厉害,用户体验很差,主要原因还是很多开发工程师并不熟练,很多项目缺少资深的项目经理,在开发过程中把关于我们、以及帮助这些页面,还有很多用户使用少的模块,都写在了本地,使得整个App开发完成后,安装包非常大,一般都在50M以上(大家可以打开自己的苹果手机看看自己下载的App占了多大的空间),推广困难,更新维护麻烦,体验很差,使得大量的推广费用花出去了,转化却是很低,这些都是开发导致的问题。

所以说开发App需要对移动互联网行业做深入的了解,选择更优化的App开发方法,合理搭配开发人力,降低开发成本,比如把一些核心功能写成本地端的,把一些不常用的模块写成H5的,将原生和H5混合使用,这样就可以降低开发成本,提高App的产品体验,优化App包的大小,这样降低了App的推广成本。

当然了,有了互联网思维,你要想注册公司,交社保,法务咨询……在当下的创业环境里,都可以寻找到这样很多的2B服务商来帮你完成,这些服务商都专注一块领域,做的比较深,值得使用,也就是说你可以足不出户,只要把这些优质的服务机构整合在一块,让他们帮你办事,你就可以做一个项目出来。

在移动互联网时代,做项目,做App就要用移动互联网思维,不仅要纵向了解,还要横向了解泛度和深度都要做到,这样我们去做项目创立公司才能节省成本,否则只能是一叶障目,不见森林。 收起阅读 »

iGeek Camp第四期--北京站圆满举行,讲师们使用的PPT已经上传,快来点击下载吧

  先传几张活动现场图片 亲们感受下!     讲师PPT 在下方 附件下载哦!    
 
先传几张活动现场图片 亲们感受下!
 
 
讲师PPT 在下方 附件下载哦!



mmexport1452341121772.jpg




mmexport1452341125256.jpg




mmexport1452341128042.jpg




mmexport1452341131797.jpg




img-97cb4c394f500426469476680869610a.jpg




img-b1c2d39410d661c0327c81e4ba426ac9.jpg




img-a0e1fd56e414de7e8273c2deec24cc51.jpg


 
 

iOS开发经常用到的图片加载库简介

   在iOS开发中几乎每个APP都会用到关于图片加载的地方,如果说仅仅加载一两张图片的话,很简单只用苹果官方提供给我们的方法:将从网络上加载的图片转化为data,再将调用imageWithData方法。但是如果我们需要加载大量的网络的图片,比如说通过列表形式...
继续阅读 »
   在iOS开发中几乎每个APP都会用到关于图片加载的地方,如果说仅仅加载一两张图片的话,很简单只用苹果官方提供给我们的方法:将从网络上加载的图片转化为data,再将调用imageWithData方法。但是如果我们需要加载大量的网络的图片,比如说通过列表形式展示一列从网络获取的图片,之前的方法就会因为在APP的主线程中加入了耗时操作(加载转化网络图片),这时我们又可以通过GCD开辟子线程的方法,解决界面卡顿的问题,但是同样这样的方法依然有不方便的地方,因为这种方法获取的网络图片并没有缓存到本地,在每次图片加载时都需要从网络获取,这种方法会消耗用户大量的流量。最终我从网上找到了可以完美解决这些问题的方法:SDWebimage第三方库,此第三方库里提供了异步缓存加载网络图片,并且具有加载GIF图等功能。
    下面就简单介绍一下SDWebimage的简单用法:
    1.在库中找到UIImageView+WebCache.h文件,调用这个方法- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;可以实现图片的异步缓存加载,并且可以同时添加占位符图片。
   2.在UIImage+GIF.h文件中,这个方法+ (UIImage *)sd_animatedGIFNamed:(NSString *)name,可以实现异步加载本地DIF图片。
  除了这些简单的用法之外SDWebimage还封装了许多加载图片的方式,比如说通过设置其中的一些属性,就可也实现图片的不同缓存方式,以及加载图片时的进度显示等等。

屏幕快照_2016-01-08_下午7.01_.51_.png

收起阅读 »

一个简单的俄罗师方块

简单的俄罗师方块下面是部分程序,我把整体的上传到附件了 #import #import "AppController.h" #import "cocos2d.h" #import "EAGLView.h" #import "AppD...
继续阅读 »

简单的俄罗师方块下面是部分程序,我把整体的上传到附件了
#import

#import "AppController.h"

#import "cocos2d.h"

#import "EAGLView.h"

#import "AppDelegate.h"




#import "RootViewController.h"




@implementation AppController




@synthesize window;

@synthesize viewController;




#pragma mark -

#pragma mark Application lifecycle







static AppDelegate s_sharedApplication;




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {




    // Override point for customization after application launch.




    // Add the view controller's view to the window and display.

    window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

    EAGLView *__glView = [EAGLView viewWithFrame: [window bounds]

                                     pixelFormat: kEAGLColorFormatRGBA8

                                     depthFormat: GL_DEPTH_COMPONENT16

                              preserveBackbuffer: NO

                                      sharegroup: nil

                                   multiSampling: NO

                                 numberOfSamples:0 ];




    // Use RootViewController manage EAGLView

    viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];

    viewController.wantsFullScreenLayout = YES;

    viewController.view = __glView;




    // Set RootViewController to window

    if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)

    {

        // warning: addSubView doesn't work on iOS6

        [window addSubview: viewController.view];

    }

    else

    {

        // use this method on ios6

        [window setRootViewController:viewController];

    }

    

    [window makeKeyAndVisible];




    [[UIApplication sharedApplication] setStatusBarHidden: YES];




    cocos2d::CCApplication::sharedApplication()->run();

    return YES;

}







- (void)applicationWillResignActive:(UIApplication *)application {

        cocos2d::CCDirector::sharedDirector()->pause();

}




- (void)applicationDidBecomeActive:(UIApplication *)application {

   

    cocos2d::CCDirector::sharedDirector()->resume();

}




- (void)applicationDidEnterBackground:(UIApplication *)application {

    

    cocos2d::CCApplication::sharedApplication()->applicationDidEnterBackground();

}




- (void)applicationWillEnterForeground:(UIApplication *)application {

    

    cocos2d::CCApplication::sharedApplication()->applicationWillEnterForeground();

}




- (void)applicationWillTerminate:(UIApplication *)application {

    }







#pragma mark -

#pragma mark Memory management




- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {

    

     cocos2d::CCDirector::sharedDirector()->purgeCachedData();

}







- (void)dealloc {

    [super dealloc];

}







@end
  收起阅读 »

APP测试基本流程

  一、 测试周期 app测试周期一般为两周,根据项目情况以及版本质量可适当缩短或延长测试时间。正式测试前先向主管或产品经理确认项目排期。 二、测试资源 测试任务开始前,检查各项测试资源。 产品功能需求文档 产品原型图 产品效果图 行为统计分析定...
继续阅读 »
 
一、 测试周期

app测试周期一般为两周,根据项目情况以及版本质量可适当缩短或延长测试时间。正式测试前先向主管或产品经理确认项目排期。

二、测试资源

测试任务开始前,检查各项测试资源。

产品功能需求文档

产品原型图

产品效果图

行为统计分析定义文档

测试设备(ios3.1.3-ios5.0.1;Android1.6-Android4.0;Winphone7.1及以上;Symbian v3/v5/Nokia Belle等)

其他(例如有秒杀专题的项目,需要规划秒杀时间表;有优惠券使用的项目,需要申请添加优惠券数据;支付宝/银联支付功能的项目,需要提前申请支付宝/银联账户等等)

二、测试要点

接收版本

本人觉得,这个过程可以直接略过。非专业测试着,不喜勿拍。

UI测试

A)  确保手头的原型图与效果图为当前最新版本。

B)  确保产品UI符合产品经理制定的原型图与效果图。

C)  一切界面问题以效果图为准,若有用户体验方面的建议,必须先以邮件或口头的形式询问产品经理。

D)由于测试环境中的数据为模拟数据,测试时必须预先想到正式环境中可能出现的数据类型。

功能测试

A)  确保手头的功能需求文档为当前最新版本。

B)  确保所有的软件功能都已实现且逻辑正常。

C)  一切功能问题以需求文档为准,若有用户体验方面的建议,必须先以邮件或口头的形式询问产品经理。个人建议,用户体验方面的建议,优先级放在修复bug之后。

D)若有些功能在技术上难以实现或者由于排期的原因无法在短时间内实现,必须得到产品经理的确认,而不是单单只听开发人员的技术解释。此处确认最好以邮件形式存在。

E)所有的“外部原因”问题,都需要尽早地督促开发人员与客户服务端人员联系协调解决。并在之后的测试报告中予以体现。

F)所有的“设计如此”、“延期处理”问题,都需要和产品经理确认后再进行验证。并在之后的测试报告中予以体现。

G)测试下单时,注册的测试账号必须符合公司规范;收货地址必须包含“测试”关键字,最好每次下单的名称中含有日期,以便查询;在正式环境中下单后必须取消该订单等。

兼容测试/性能测试

A)  确保软件在所有兼容机型上都能正常使用(ios一般需要兼容7或者6,  ios5可以不用,用户使用率已经低于5%以下)

B)  对于低端性能兼容机上独有的问题(例如ios5以下、Android1.6以下),若在技术上难以修改或者由于排期的原因无法在短时间内改进,必须在测试日报中注明,并得到技术平台主管、产品经理以及运营人员的确认,最好以邮件的形式得到确认)

C)  性能测试方面必须满足硬件压力条件下的测试需要(例如多线程,用户常用的app都要后台运行的环境中测试。)

D)网络响应用户体验方面的性能测试,需要保证在wifi、3g、2g网络下的切换效果。比如wifi切换到2g,网络响应的速度以及切换界面。

后台订单统计测试

A)  核对“客户端相关启动查询”项,此项数据就是经常说的“激活量”,非常重要。测试时必须保证该项中的各数据均正确,且每次启动软件都会有相应的统计记录。

B)  核对“订单查询”项,测试时必须保证各数据均正确,且每次成功下单后都会有相应的统计记录。

C)  需要注意的是,在成功下单之后,后台会做判断将该订单划到测试订单范围,测试人员必须到“订单查询(测试)”模块中核对订单统计记录信息。

用户行为统计测试

A)确保手头的行为统计分析定义文档为最新版本,且与开发人员手中的文档一致。

B)确保产品经理在文档中所定义的页面在该产品中都是存在的。

C)尽可能真实地模拟用户行为。

D)核对统计日志,确保各项操作所对应的页面ID以及操作ID都是正确的。

回归测试

A)软件最终上线前,需对产品进行回归测试,测试内容包含之前所有的测试项目

B)回归测试不再对细节进行测试,而是类似于对产品进行验收,从客户正常使用的角度对产品进行再一轮的整体测试。

C)只有在回归测试通过之后,才对产品进行提交。

三、测试日报及产品上线报告

测试人员每天需对所测项目发送测试日报。

测试日报所包含的内容为:

A)对当前测试版本质量进行分级。

B)对较严重的问题进行例举,提示开发人员优先修改。

C)对版本的整体情况进行评估。

产品上线前,测试人员发送产品上线报告

 现使用Testbird进行app自动化测试,省时省事省钱~~~

整理:人人都是产品经理  lemongrass

转载自:人人都是产品经理  收起阅读 »

解决很多人的程序崩溃问题,可以追踪到程序崩溃的位置

  如何自动定位到程序崩溃的地方(iOS Xcode 自带crash 崩溃问题的追踪方法)       这里不知道图片为啥粘不上去,只有传到附件了,下面的图都是按照上面先后顺序排列的,我把整个文档传到附件里   一、Exception break...
继续阅读 »
 
如何自动定位到程序崩溃的地方(iOS Xcode 自带crash 崩溃问题的追踪方法)
 
 
 
这里不知道图片为啥粘不上去,只有传到附件了,下面的图都是按照上面先后顺序排列的,我把整个文档传到附件里
 
一、Exception breakpoint 的添加
 
1、切换到breakpoint 视图界面(第一张图)
 


79D1EDBC-1C55-4121-BBC1-8C49A2250582.png


 
 
 
 
2、点击最底端的"+"按钮,添加Add Exception BreakPoint,这个就是捕获所有的exception, 貌似stackoverflow上说,bad_access那种错误无法捕获的,这个用于捕获那些SIGSEGV 的错误。 (第二张图)
 
 
 

无标题.png


 
 
 
3、添加完成之后的界面(第三张图)

无标题1.png


 
 
 
 
二、Symbolic breakpoint的添加 (第四张图)
 
 第二步选择的时候选 Add Symbolic BreakPoint  
第三步截图;添加完成之后添加上objc_exception_throw
 
 


无标题.png


 
 
 
 
 
 
 
 
 
 
  收起阅读 »

Android V2.2.5 2015-12-31版本更新

 环信的小伙伴们,大家好。 2015-12-31 环信发布了最新的安卓SDK版本V2.2.5,大家可以关注一下,新版本的功能优化点如下:   1)实时通话新增弱网监测、暂停或打开音频视频流等API(相应增加的方法可查看文档)。 2)优化实时通话音质,完善了语...
继续阅读 »
 环信的小伙伴们,大家好。
2015-12-31 环信发布了最新的安卓SDK版本V2.2.5,大家可以关注一下,新版本的功能优化点如下:
 


1)实时通话新增弱网监测、暂停或打开音频视频流等API(相应增加的方法可查看文档)。
2)优化实时通话音质,完善了语音编码算法,提高了语音清晰度。 
3)在小米手机上,im离线时支持使用小米推送来进行消息的推送。
4)GCM优化,手机切到后台一段时间后,在支持GCM的app及手机上sdk会主动断掉和im长连接,
消息通过GCM推送到客户端,使手机更省电 。
5)修复实时通话对方拒绝时,有时候不显示拒绝的bug。
 


欢迎大家下载体验,下载地址http://www.easemob.com/downloads
 
使用过程中,有任何问题建议欢迎在下方评论直接回复留言! 收起阅读 »

教你如何从零开始,用环信ios sdk实现即时视频和聊天

先上效果图: 说说需求:开发一个可以进行即时视频聊天软件. 1. 最近比较忙,考完试回到公司就要做这个即时通信demo.本来是打算用xmpp协议来做视频通信的,想了想要搞后台,还要搭建服务器.一开始没明白是怎么样的一种形式.(现在想了想,其实就是...
继续阅读 »
先上效果图:

271180-95dcd33d9f72fb53-2.png




说说需求:开发一个可以进行即时视频聊天软件.
1. 最近比较忙,考完试回到公司就要做这个即时通信demo.本来是打算用xmpp协议来做视频通信的,想了想要搞后台,还要搭建服务器.一开始没明白是怎么样的一种形式.(现在想了想,其实就是自己写个服务器,然后放在服务器上而已了""脑袋被驴踢了).让后问boss服务器是我自己写还是怎样?然后boss让我先做个环信的demo,搞完再搞xmpp.


271180-738447d9a9e2d25a.png


[服务器的安装包]

2. 环信,什么鬼?
 - 1.集成IOS SDK前的准备工作: 
(如果需要推送消息,则要到苹果官网上制作证书,再到环信后台制作推送证书.
详细请看http://www.easemob.com/docs/ios/IOSSDKPrepare/#registerDeveloper)
注册环信开发者账号并创建后台应用,
登陆地址:https://console.easemob.com/?comeFrom=easemobHome
注册和登陆就不多说了,只介绍创建应用: 一般我们选择开放注册.
![创建应用]


271180-1dddbf42ba6c6d3c.jpeg



![得到appkey]


271180-cecf5150801dde87.png


 
   - 2.然后开始创建工程:
下载环信Demo及SDK:http://www.easemob.com/sdk/
解压缩iOSSDK.zip后会得到以下目录结构:


271180-caab5f8cb92c423b.jpeg



将EaseMobSDK拖入到项目中,并添加SDK依赖库


271180-c9c98270a35e53a5.jpeg



添加 以后,在AppDelegate中注册SDK

```
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary     *)launchOptions
{//registerSDKWithAppKey:为应用标示,apnsCertName为推送证书;(如果没用推送证书,这里可以随便)
    [[EaseMob sharedInstance] registerSDKWithAppKey:@"easemob-demo#chatdemo" apnsCertName:apnsCertName];
    // 需要在注册sdk后写上该方法
    [[EaseMob sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
    return YES;
}
```

3.下面我们开始主要界面的详解:
先上图吧.(因为视频通信需要两台真机,而我只能和iPhone对模拟器,所以没有视频图,但demo已经过测试可以视频聊天)
 - 1. 要进行对话,必须先有用户名,这样才能让对方找到你.(先注册,再登录.退出(用于测试))
![登录页面]

271180-a0f0c34d6cdc8d33.png



登录页面的代码比较简单.

    ```
           //异步注册账号
         [[EaseMob sharedInstance].chatManager asyncRegisterNewAccount:name.text
                                                             password:password.text
                                                       withCompletion:
         ^(NSString *username, NSString *password, EMError *error) {
             
             if (!error) {//注册成功,显示马上登录
                 UIAlertView* alert = [[UIAlertView alloc] initWithTitle:nil
                                                                 message:@"注册成功,请登陆"
                                                                delegate:nil
                                                       cancelButtonTitle:@"OK"
                                                       otherButtonTitles:nil];
                 [alert show];
                 alert = nil;
             }else{
                //输出错误信息.
             }
         } onQueue:nil];
//////////////////////////////////////////////////////////////////////
           
           // //异步登录账号
            [[EaseMob sharedInstance].chatManager asyncLoginWithUsername:name.text
                                                            password:password.text
                                                          completion:
         ^(NSDictionary *loginInfo, EMError *error) {
             
             if (loginInfo && !error) {
                 //设置是否自动登录
                 [[EaseMob sharedInstance].chatManager setIsAutoLoginEnabled:YES];
                 // 旧数据转换 (如果您的sdk是由2.1.2版本升级过来的,需要家这句话)
                 [[EaseMob sharedInstance].chatManager importDataToNewDatabase];
                 //获取数据库中数据
                 [[EaseMob sharedInstance].chatManager loadDataFromDatabase];
                 //获取群组列表
                 [[EaseMob sharedInstance].chatManager asyncFetchMyGroupsList];
             #warning 开始跳转,然后开始聊天
                 NSLog(@"登录成功");
                 TTAlertNoTitle(@"登录成功");
                 [self thisToChatViewController];
                 //发送自动登陆状态通知
                 //[[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_LOGINCHANGE object:@YES];
                 
             }
             else
             {//输出错误信息.(太多,所以详见demo)
             }
         } onQueue:nil];

      ```

 - 2.登录后,有挺多的控件,
     - 1.返回键:点击时会调用-(void)dealloc方法,用做登出操作.
     - 2.UITextField: 你想要发送对象的名称,例如8080(下面将用8080做例子).
     - 3.send:建立一个与8080的会话.
     - 4.cheak:用于测试(现在没用了)
     - 5.hehe黄色的lable: 当有收到消息的时候,文字内容就会发生改变,显示还有多少条未读信息.
     - 6.最下面是UITableview,显示会话联系人.
![聊天列表]


271180-a5d5ab0f171b12b7.png




  
```
    首先要将代理设置好
    [[EaseMob sharedInstance].chatManager removeDelegate:self];
    //注册为SDK的ChatManager的delegate
    [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
    [[EaseMob sharedInstance].callManager addDelegate:self delegateQueue:nil];
    //最后一个为即时通讯的代理,(即时视频,即时语音)

    当离开这个页面的时候,要讲代理取消掉,不然会造成别的页面接收不了消息.
    [[EaseMob sharedInstance].chatManager removeDelegate:self];
    [[EaseMob sharedInstance].callManager removeDelegate:self];

    这样以后,就可以使用代理方法来作一些事情了
    1.接收消息
    -(void)didReceiveMessage:(EMMessage *)message
    2. 未读消息数量变化回调
    -(void)didUnreadMessagesCountChanged
    3.实时通话状态发生变化时的回调,(如果没有实现这个函数,视频聊天邀请就会接收不到.)
    - (void)callSessionStatusChanged:(EMCallSession *)callSession changeReason:(EMCallStatusChangedReason)reason error:(EMError *)error
```

 - 3.(已经更新)因为是demo,就先用UITextView来显示对话,在UITextField中输入文字,点击send,即可发送,对方发送过来的内容也可以看到.点击视频聊天,大概要等5-10s对方才能收到请求.


271180-95dcd33d9f72fb53-2.png




```
   发送消息
-(void) send:(UIButton *)sender{
//         conversation= [[EaseMob sharedInstance].chatManager conversationForChatter:@"ozxozx" conversationType:eConversationTypeChat];
    
    
    EMChatText *txtChat = [[EMChatText alloc] initWithText:sendContext.text];
    EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithChatObject:txtChat];

    // 生成message
    EMMessage *message = [[EMMessage alloc] initWithReceiver:userName bodies:@[body]];
    message.messageType = eMessageTypeChat;
    

    EMError *error = nil;
    
    id chatManager = [[EaseMob sharedInstance] chatManager];
//    [chatManager asyncResendMessage:message progress:nil];
    [chatManager sendMessage:message progress:nil error:&error];
    if (error) {
        UIAlertView * a = [[UIAlertView alloc] initWithTitle:@"error" message:@"发送失败" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
        [a show];
    }else {
        textview.text = [NSString stringWithFormat:@"%@\n\t\t\t\t\t我说:%@",textview.text,sendContext.text];
    }
}
```

```
 //从会话管理者中获得当前会话.并将会话内容显示到textview中
-(void) addtext{
//1.
    EMConversation *conversation2 =  [[EaseMob sharedInstance].chatManager conversationForChatter:userName conversationType:0] ;
    NSString * context = @"";//用于制作对话框中的内容.(现在还没有分自己发送的还是别人发送的.)
    NSArray * arrcon;
    NSArray * arr;
    long long timestamp = [[NSDate date] timeIntervalSince1970] * 1000 + 1;//制作时间戳
    arr = [conversation2 loadAllMessages]; // 获得内存中所有的会话.
    arrcon = [conversation2 loadNumbersOfMessages:10 before:timestamp]; //根据时间获得5调会话. (时间戳作用:获得timestamp这个时间以前的所有/5会话)
// 2.
    for (EMMessage * hehe in arrcon) {
        id messageBody = [hehe.messageBodies firstObject];
        NSString *messageStr = nil;
//3.
        messageStr = ((EMTextMessageBody *)messageBody).text;
//        [context stringByAppendingFormat:@"%@",messageStr ];
        
        if (![hehe.from isEqualToString:userName]) {//如果是自己发送的.
            context = [NSString stringWithFormat:@"%@\n\t\t\t\t\t我说:%@",context,messageStr];
        }else{
            context = [NSString stringWithFormat:@"%@\n%@",context,messageStr];
        }
        
    }
    
    textview.text = context;
}
```
1 .EMConversation *conversation2 :会话对象,里面装着当前对话的双方的各种消息(EMMessage).
2 . EMMessage 消息.

```
 一个message的内容(对方发来的)
{"messageId":"83265683047055820","messageType":0,"from":"ozx8899","bodies":
["{\"type\":\"txt\",\"msg\":\"反对党\"}"]
,"isAcked":true,"to":"ozxozx","timestamp":1436951601011,"requireEncryption":false}
```

3.```((EMTextMessageBody *)messageBody)即为"bodies":["{\"type\":\"txt\",\"msg\":\"反对党\"}"]
即:消息为文本,信息内容为:反对党```


##视频聊天
```
-(void)openTheVideo:(UIButton *)btn{
    BOOL isopen = [self canVideo];//判断能否打开摄像头,(太多,详见demo)
    EMError *error = nil;
    EMCallSession *callSession = nil;
    if (!isopen) {
        NSLog(@"不能打开视频");
        return ;
    }
    //这里发送异步视频请求
    callSession = [[EaseMob sharedInstance].callManager asyncMakeVideoCall:userName timeout:50 error:&error];
    //请求完以后,开始做下面的
    if (callSession && !error) {
        [[EaseMob sharedInstance].callManager removeDelegate:self];
        CallViewController *callController = [[CallViewController alloc] initWithSession:callSession isIncoming:NO];
        callController.modalPresentationStyle = UIModalPresentationOverFullScreen;
        [self presentViewController:callController animated:NO completion:nil];
    }
    
    if (error) {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"error", @"error") message:error.description delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil];
        [alertView show];
        alertView = nil;
    }
}
```

视频聊天的代码主要在CallViewController中.如果需要改变视频通话时的界面,就要改此控制器中的布局内容.
最要值得主要的是在dealloc函数中一定要加下面两句
```
[[EaseMob sharedInstance].callManager removeDelegate:self];
[[NSNotificationCenter defaultCenter] postNotificationName:@"callControllerClose" object:nil];
```
1.注销callManager的代理   2.通知消息中心,即时对话已经完成了
如果没有第二句,那么你的程序就只能进行一次即时通讯.
并且要在CallViewController将要返回的控制器中加上函数,注册当前控制器为callManager的代理.
```  
- (void)callControllerClose:(NSNotification *)notification
{
    [[EaseMob sharedInstance].callManager addDelegate:self delegateQueue:nil];
}
```

如果开发过程中遇到问题,不妨看看是不是两个主要的代理没有设定好:
[EaseMob sharedInstance].chatManager ; //这是会话管理者,获取该对象后, 可以做登录、聊天、加好友等操作
[EaseMob sharedInstance].callManager ;//这是即时通讯(语音聊天和视频聊天)的管理者.

demo可以在:https://github.com/ouzhenxuan/huanxinDemo 下载
使用前,请到http://www.easemob.com/downloads 把iOS版的SDK下载到工程中,即可使用 
简化版的demo:https://github.com/ouzhenxuan/shipinchat
如果感觉对你有帮助,请点个star.最近十分需要.你的star是我工作的支持. 收起阅读 »

火车票余票的查询+上拉加载,下拉刷新

上拉刷新,下拉加载demo。其中用到了MJRefresh第三方库,可以用于所有列表形式的刷新和加载,对于程序开发者具有很大的帮助; 使用方法:1.在ViewController中找到集成控件,设置刷新和加载时视图显示的提示信息 利用json解析网络接口,将解析...
继续阅读 »
上拉刷新,下拉加载demo。其中用到了MJRefresh第三方库,可以用于所有列表形式的刷新和加载,对于程序开发者具有很大的帮助; 使用方法:1.在ViewController中找到集成控件,设置刷新和加载时视图显示的提示信息
利用json解析网络接口,将解析出来的数据用视图显示出来,这里下拉刷新与上拉加载可以运用到多个地方,解决了数据一下子不能展示更多的缺陷
下面知识部分代码,其他代码已传到附件了
#import "AppDelegate.h"
#import "AppViewController.h"
@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

AppViewController *tt = [[AppViewController alloc]init];


self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = tt;
[self.window makeKeyAndVisible];
return YES;
}

#import "AppViewController.h"
#import "CarViewController.h"
#import "AppDelegate.h"
@interface AppViewController ()

@end

@implementation AppViewController

- (void)viewDidLoad {
[super viewDidLoad];
CarViewController *tt1 = [[CarViewController alloc]init];

UINavigationController *nar = [[UINavigationController alloc]initWithRootViewController:tt1];
UIApplication *app = [UIApplication sharedApplication];
AppDelegate *app2 = app.delegate;
app2.window.rootViewController = nar;
}

#import "CarViewController.h"
#import "AppDelegate.h"
#import "CalendarHomeViewController.h"
#import "CalendarViewController.h"
#import "ChaViewController.h"
//CalendarMonthHeaderView.m
#import "Color.h"
#define WIDTH [[UIScreen mainScreen]bounds].size.width
#define HEIGHT [[UIScreen mainScreen]bounds].size.height
#define H [[UIScreen mainScreen]bounds].size.height/100
#define W [[UIScreen mainScreen]bounds].size.width/100

@interface CarViewController ()
{
UITextField *goText;
UITextField *toText;
CalendarHomeViewController *chvc;
UILabel *dateLable1;
NSString *strDate;
NSString *strWeek;
id obj;
//NSMutableString *isStr;

}

@end

@implementation CarViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.

// self.navigationController.navigationBar.backgroundColor = COLOR_THEME;
self.navigationController.navigationBar.barTintColor = COLOR_THEME;
self.navigationItem.title = @"车票查询";

self.view.backgroundColor = [UIColor colorWithRed:96.0/255 green:105.0/255 blue:144.0/255 alpha:0.31];
//出发点
goText = [[UITextField alloc]initWithFrame:CGRectMake(8*W, 20*H, 26.7*W, 7.5*H)];
goText.backgroundColor = [UIColor groupTableViewBackgroundColor];
goText.placeholder = @"出发地";
goText.font = [UIFont systemFontOfSize:24];
goText.textAlignment = NSTextAlignmentCenter;
goText.layer.cornerRadius = 10.0;
goText.delegate = self;
toText = [[UITextField alloc]initWithFrame:CGRectMake(66*W, 20*H, 26.7*W, 7.5*H)];
toText.backgroundColor = [UIColor groupTableViewBackgroundColor];
toText.placeholder = @"目的地";
toText.textAlignment = NSTextAlignmentCenter;
toText.font = [UIFont systemFontOfSize:24];
toText.delegate = self;
toText.layer.cornerRadius = 10.0;

[self.view addSubview:goText];
[self.view addSubview:toText];
//箭头
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(40*W,21*H, 19*W, 5*H);
[button setImage:[UIImage imageNamed:@"1"] forState:UIControlStateNormal];
[button addTarget:self action:@selector(change) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];

//选择日期

UIButton *but2 = [[UIButton alloc]initWithFrame:CGRectMake(0, 36*H, self.view.frame.size.width, 50)];
but2.backgroundColor = COLOR_THEME;
but2.titleLabel.textColor = [UIColor whiteColor];
but2.tag = 2;
//[but2 setTitle:@"选择日期" forState:UIControlStateNormal];
[but2 addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:but2];


UILabel *dateLable = [[UILabel alloc]initWithFrame:CGRectMake( 8*W, 2*H, 22*W, 4*H)];
dateLable.text = @"出发日期";

dateLable1 = [[UILabel alloc]initWithFrame:CGRectMake( 40*W, 2*H, 60*W, 4*H)];

[dateLable1 setTextColor:[UIColor whiteColor]];
[dateLable setTextColor:[UIColor whiteColor]];
[but2 addSubview:dateLable];
[but2 addSubview:dateLable1];

//NSString *s = strDate;


//查询按钮

UIButton *but = [[UIButton alloc]initWithFrame:CGRectMake(37*W, 60*H, 100, 50)];
but.backgroundColor = COLOR_THEME;
but.titleLabel.textColor = [UIColor whiteColor];
[but setTitle:@"查询" forState:UIControlStateNormal];
[but addTarget:self action:@selector(cha) forControlEvents:UIControlEventTouchUpInside];
[but.layer setCornerRadius:10.0];
[self.view addSubview:but];

}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{

[textField resignFirstResponder];
return YES;
}

-(void)change{

NSString *st1 = goText.text;
NSString *st2 = toText.text;

goText.text = st2;
toText.text = st1;
}

-(void)cha{
ChaViewController *cha = [[ChaViewController alloc]init];

cha.goStr = goText.text;
cha.toStr = toText.text;
cha.strDate1 = strDate;
cha.strWeek1 = strWeek;

[self presentViewController:cha animated:YES completion:nil];













}
-(void)click:(UIButton *)but
{

if (!chvc) {

chvc = [[CalendarHomeViewController alloc]init];

chvc.calendartitle = @"飞机";

[chvc setAirPlaneToDay:365*3 ToDateforString:nil];//飞机初始化方法

}

chvc.calendarblock = ^(CalendarDayModel *model){

NSLog(@"\n---------------------------");
NSLog(@"1星期 %@",[model getWeek]);
NSLog(@"2字符串 %@",[model toString]);
NSLog(@"3节日 %@",model.holiday);
strDate = [model toString];
strWeek = [model getWeek];
if (model.holiday) {

//[but setTitle:[NSString stringWithFormat:@"%@ %@ %@",[model toString],[model getWeek],model.holiday] forState:UIControlStateNormal];


dateLable1.text = [NSString stringWithFormat:@"%@ %@ %@",[model toString],[model getWeek],model.holiday];

}else{

//[but setTitle:[NSString stringWithFormat:@"%@ %@",[model toString],[model getWeek]] forState:UIControlStateNormal];
dateLable1.text = [NSString stringWithFormat:@"%@ %@",[model toString],[model getWeek]];
}
};

[self.navigationController pushViewController:chvc animated:YES];




}

收起阅读 »

入手环信,代码块知识点用的多,发帖回顾下

#import int c = 0; int main(int argc, const char * argv[]) {     @autoreleasepool {         //block声明格式:返回值类型 (^blaock名字) (形参列表...
继续阅读 »

#import

int c = 0;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //block声明格式:返回值类型 (^blaock名字) (形参列表)
        int (^myFun)();
       
        //block实现: block 名字 = ^(形参列表){};
        myFun =  ^(){
            NSLog(@"这是一个代码块");
            return 1;
        };
       
        //调用:block名称(实参列表),有返回值的block,可以用一个变量进行接收
        int a = myFun();//空括号不能少!!
       
        NSLog(@"%d",a);
       
        //有返回值,有形参,声明和实现放一起
        int (^myBlock)(int a, int b) = ^(int a, int b){
            return 1+b;
        };
        //调用
        int sum = myBlock(10,20);
        NSLog(@"%d",sum);
       
        //返回值类型是字符串 NSString *(^名字)(形参列表)
       
        NSString *(^myBlock1) (NSString *s) = ^(NSString *s){
            NSLog(@"字符串:%@",s);
            return s;
        };
        myBlock1(@"123");
       
        //有一个局部变量,要在block进行值的改变
       
        __block int b = 0;
       
        void (^myBlock2)() = ^(){
            b++;
        };
       
        //有一个全局变量,在block进行值的改变
       
        void (^myBlock3)() = ^(){
            c++;
        };
    }
    return 0;
}

block 作为函数参数
block作为形参使用,在函数中使用block方法运算数据
#import
//block作为函数的参数
//函数返回值类型  函数名(block的声明格式)
void fun(int (^bsasa)(int a,int b)){
    int sum = bsasa(3,2);
    NSLog(@"%d",sum);
}

void fun1(NSString *(^myBlock)(NSString *s),NSString *s1){
    NSLog(@"---%@",myBlock(s1));
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        //当一个block作为函数的参数是,其返回值类型、形参个数及类型要与函数形参格式保持一致
        int (^myBlock) (int c, int d) = ^(int a, int b){
            NSLog(@"--%d,%d",a,b);
            return a-b;
        };
       
        //函数形参是block,调用时,直接传block的名字就行
        fun(myBlock);
       
        //调用的另一种方式:内联
        //内联block格式:^返回值类型 (形参列表){}
        fun(^int(int a, int b) {
           
            return a*b;
        });
       
       
        fun1(^NSString *(NSString *s) {
            return s;
        }, @"123");
       
    }
    return 0;
}
block作为方法的参数

Person.h
#import

//使用typedef声明block:typedef 返回值类型 (^名字)(形参列表)
typedef int  (^myblockType)(int a);

@interface Person : NSObject

//block作为方法的参数:(返回值类型 (^)(形参列表))参数名(blcok名字)
-(void)myblock:(int (^)(int a))block;

-(void)sengStr:(NSString *)name andblock:(NSString *(^)(NSString *))myblock1;

-(void)useblockType:(myblockType)sss;

-(void)myname:(NSString *)name;

@end
Person.m
#import "Person.h"

@implementation Person

-(void)myblock:(int (^)(int))block{
   
    int a = block(10);
    NSLog(@"%d",a);
}

-(void)sengStr:(NSString *)name andblock:(NSString *(^)(NSString *))myblock1{
    myblock1(name);
}

-(void)useblockType:(myblockType)sss{
    int res = sss(10);
    NSLog(@"---%d",res);
}

-(void)myname:(NSString *)name{
   
    NSString *str = [name stringByAppendingString:@"+-"];
    NSLog(@"%@",str);
}

@end
main.m
#import

#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
   
        Person *p = [[Person alloc] init];

        [p sengStr:@"这儿" andblock:^NSString *(NSString *s) {
            NSLog(@"我在:%@",s);
            return s;
        }];//内联形式直接实现block、
       
        [p useblockType:^int(int a) {//typedef 声明
            return a;
        }];  
       
        //传入一个名字,然后进行拼接,名字--;
       
        [p myname:@"aa"];//
       
        [p sengStr:@"aa" andblock:^NSString *(NSString *s) {//
            return [s stringByAppendingString:@"--"];
        }];
       
    }
    return 0;
}
  收起阅读 »

基于react native 和 环信的实时通话的免费“店话” 项目 开源了

    基于react native 和 环信的实时通话的免费“店话” 项目开源啦! 用“店话”搜索想要找的店铺电话,也可以上传自己的店铺信息和在线的客户免费语音通话,语音通话的流量仅仅需要3-5kb/s啊,还不快快下载!   扫描二维码下载体验(ap...
继续阅读 »
 
 
基于react native 和 环信的实时通话的免费“店话” 项目开源啦!



用“店话”搜索想要找的店铺电话,也可以上传自己的店铺信息和在线的客户免费语音通话,语音通话的流量仅仅需要3-5kb/s啊,还不快快下载!
 



扫描二维码下载体验(apk在附件已上传,可以直接安装哦)



QQ截图20160106174752.png




源代码:https://github.com/vasth/dianhua(如果喜欢可以点下方赞赏按钮哦☺)

 
APP运行效果图展示:



Screenshot_2016-01-06-15-48-38-077_店话.png




Screenshot_2016-01-06-15-48-50-005_店话.png




Screenshot_2016-01-06-15-48-52-899_店话.png




Screenshot_2016-01-06-15-52-17-944_店话.png



 
点击下载APK  ↓
 
  收起阅读 »

ios简单的上拉刷新,下拉加载demo

ios简单的上拉刷新,下拉加载demo。其中用到了MJRefresh第三方库,可以用于所有列表形式的刷新和加载,对于程序开发者具有很大的帮助; 使用方法:1.在ViewController中找到集成控件,设置刷新和加载时视图显示的提示信息; 2.在设置刷新视图...
继续阅读 »
ios简单的上拉刷新,下拉加载demo。其中用到了MJRefresh第三方库,可以用于所有列表形式的刷新和加载,对于程序开发者具有很大的帮助; 使用方法:1.在ViewController中找到集成控件,设置刷新和加载时视图显示的提示信息; 2.在设置刷新视图中开辟线程的方法里设置刷新视图出现的时间长度; 3.在设置加载尾视图中设置加载时,将要增加的信息数量。 最后大家还可以根据自己的需求另作改动。


Simulator_Screen_Shot_2016年1月6日_下午1.07_.50_.png




Simulator_Screen_Shot_2016年1月6日_下午1.07_.59_.png


  收起阅读 »

看到很多人都急需一个ios简单集成 的demo ,这里就献丑上传一个

这个demo是按照视频http://v.youku.com/v_show/id_XMTQyMDc0NTQwMA==.html?from=y1.7-2  集成的,真的很简单...      项目已经上传到附件,感兴趣的可以下载看看,有不明白的可以在下面跟帖   ...
继续阅读 »
这个demo是按照视频http://v.youku.com/v_show/id_XMTQyMDc0NTQwMA==.html?from=y1.7-2  集成的,真的很简单...
 
   项目已经上传到附件,感兴趣的可以下载看看,有不明白的可以在下面跟帖
 
 
附件查看不到demo 的可以通过连接下载,下载地址:点击下载 收起阅读 »

iOS通过终端进行导入第三方库

iOS
这种方法是非常实用简单的,避免依次加入多个库    先确认电脑中是否安装有Ruby(ruby是安装cocoapods的前提)。 打开终端: 打开launchpad –其它-终端。 打开后 运行代码“rvm list”(查询已经安装的Ruby);如果先终...
继续阅读 »
这种方法是非常实用简单的,避免依次加入多个库
 
 先确认电脑中是否安装有Ruby(ruby是安装cocoapods的前提)。
打开终端: 打开launchpad –其它-终端。
打开后 运行代码“rvm list”(查询已经安装的Ruby);如果先终端列出了list得话说明Mac中已经安装了ruby 就可以直接安装cocoapods了。
记住到这的时候如出现在command not found:请阅读下面ruby安装文档,如果你的终端运行代码“rvm list”出现
rvm rubies

# No rvm rubies installed yet. Try 'rvm help install'.

scsysdeMac-mini-6:~ scsys$ :请跳过ruby安装
接下来就是你安装cocoapods,好了这是前期的工作,后面就是按照cocoapods文档中方法走吧
  收起阅读 »

Chatbox - 轻量级jQuery聊天窗插件

之前做WebIM的时候苦于没有现成的聊天窗组件,所以自己封装了一个jQuery插件。该插件仅仅只是一个前端组件,不涉及后端通讯。后端请自行实现,比如可以使用环信的IM云,后期我有空可以实现一个DEMO分享出来。 1. 轻量级动画特效以及友好的界面 2....
继续阅读 »

194137_o591_820321.png



之前做WebIM的时候苦于没有现成的聊天窗组件,所以自己封装了一个jQuery插件。该插件仅仅只是一个前端组件,不涉及后端通讯。后端请自行实现,比如可以使用环信的IM云,后期我有空可以实现一个DEMO分享出来。

1. 轻量级动画特效以及友好的界面
2. 支持多窗口和实例
3. 完善的回调函数以实现自定义功能
4. 多种调用方式
5. 良好的封装以及扩展性
6. 每个聊天窗对象实例以data属性的形式附加在聊天窗DOM对象上(如果你想获得某个特定插件的实例,可以直接从页面元素中获取:$('{boxId}').data('chatbox'))

项目地址:
http://haozki.me/Chatbox/
以上地址包含完整的API文档和Live Demo。可以直接看效果。

开源中国收录地址:
http://www.oschina.net/p/Chatbox

下载地址:
https://github.com/haozki/Chatbox/archive/master.zip
  收起阅读 »

score.js - jQuery星级评分插件

一年前写的jQuery星级评分插件,现在分享出来。代码不过200来行,有完善API文档说明。 1.完善的API 2.丰富的回调接口,实现灵活的功能控制 3.轻量级代码,使用更高效 4.支持自定义图标 5.支持Font-awsome扩展 项目...
继续阅读 »

195020_hyWh_820321.png



一年前写的jQuery星级评分插件,现在分享出来。代码不过200来行,有完善API文档说明。

1.完善的API
2.丰富的回调接口,实现灵活的功能控制
3.轻量级代码,使用更高效
4.支持自定义图标
5.支持Font-awsome扩展

项目地址:
http://haozki.me/score.js/
以上地址包含API文档和Live Demo。可以直接看效果。

开源中国收录地址:
http://www.oschina.net/p/score-js

下载地址:
https://github.com/haozki/score.js/archive/master.zip
  收起阅读 »

使用环信提供的EaseUI库集成客服demo

EaseUICustomer 使用环信提供的 EaseUI 库集成客服demo,方便开发者参考集成环信的客服功能;项目有详细的注释,适合新手以及老鸟参考研究, 此demo使用最新的环信提供的 EaseUI 库集成环信的客服聊天   源代码:https://g...
继续阅读 »
EaseUICustomer

使用环信提供的 EaseUI 库集成客服demo,方便开发者参考集成环信的客服功能;项目有详细的注释,适合新手以及老鸟参考研究, 此demo使用最新的环信提供的 EaseUI 库集成环信的客服聊天
 
源代码https://github.com/lzan13/EaseUICustomer
 
运行截图如下:



7RGANY[DGAF[[CML4ANAEC.jpg




`7A_91G_I(~1P~)1FLA7X2.png




24AOP1EC[9}0E8WLR5NG4L0.png




A{BZJ]KHU~F`VVT144@KR.png




TG6`TNGLNYJ@QX)F75FM]8O.png




R0BPRN91X)_L}1E~0U~YBI.jpg




U98IAWG0NTS8@BZTS95{2T.png




Z)~OL@W@VJPXRD@A7_NE}P0.png



关于EaseUI库

EaseUI 库可以去环信官网下载界面下载sdk,里边包含有EaseUI库 官网下载sdk
或者去环信的github上去下载:官网下载sdk

特点

使用了Android Material Design 样式开发,将Android新设计与环信的EaseUI结合,

需要注意的地方(重要)
使用时需要把配置文件里的appkey改成自己的,以及开屏页SplashActivity类里默认数据改成自己的 使用时需要把配置文件里的appkey改成自己的,以及开屏页SplashActivity类里默认数据改成自己的 使用时需要把配置文件里的appkey改成自己的,以及开屏页SplashActivity类里默认数据改成自己的 重要的事情说三遍 在使用过程中有什么疑问可以去环信官方社区 `imgeek.org` 去发帖[imgeek 社区][imgeek]
实现的功能

全局消息的监听,在DemoHelper类实现,搜索 onEvent 方法

通知栏消息提醒,这里使用 EaseUI 定义封装好的通知,这里只是稍微设置了下,单条消息会显示消息内容,多条消息会显示消息条数,以及小图标的设置

用户轨迹图文消息混排

满意度消息的收发

通知栏提醒

首先说下EaseUI重构客服demo的环境以及工具版本:
AndroidStudio version:1.5.0 SDKTools version:24.4.x Build-tools version:23.0.2 Compile SDK version:API 22(5.1) Minimum SDK version:API 15(4.0.3) Gradle version:2.4 jdk version:1.7
 
AndroidStudio等工具地址:

Android 官方下载  Android官网
国内网友收集 AndroidDevTools
Gradle(AndroidStudio有时自动下载不成功,所以要自己下载)  gradle v2.4下载 收起阅读 »

信息的分类,在做购物类的app很实用这个模块,将具有相同的特征信息进行归纳,类似于好友列表的原理

在iOS开发过程中我们经常用到将信息进行不同的分类,在淘宝上像将酒水,饮料,日常用品,家具等进行分类这就可以在一个viewcontroller里面编译,并且也可以获取点击事件通过不同的层数,知道哪个区域被点击,用起来非常的方便,这里已经封装这种方法的基本框架,...
继续阅读 »
在iOS开发过程中我们经常用到将信息进行不同的分类,在淘宝上像将酒水,饮料,日常用品,家具等进行分类这就可以在一个viewcontroller里面编译,并且也可以获取点击事件通过不同的层数,知道哪个区域被点击,用起来非常的方便,这里已经封装这种方法的基本框架,你只需要往框架里面填写信息就行了,以下注意事项;
   1.建一个你需要的viewcontroller,demo上是直接用ViewController,你需要导入MultilevelMenu文件
   2.接下来,就是你对数据的进行操作,最好是将你的数据通过字典,数组像结合进行整合,第二种方法就是通过plist文件进行操作,注意plist文件的操作,个人觉得plist高大上一点。
    3.就是将你的数据进行一层一层的解析,获取不同层次的方法:rightMeun * meun=[[rightMeun alloc] init]; meun.meunName = arr;//第一层获取 //酒水的分类 //日常用品分类//食品分类//水果 //饮料在这里我是简单用字典与数组进行数据处理:然后一层一层解析,具体的方法和怎么使用都在下面的demo中。
    4.在你使用之前,一定要将MultilevelMenu文件和Vendor文件加到工程中去,如果想改变cell的大小,菜单的颜色呀,你都可以在MultilevelMenu.h找到属性。效果图见附件和demo,工程中更加详细
 
收起阅读 »

用于聊天通讯录添加好友和删除好友的小demo

iOS
用到iOS中委托知识点把修改后的值传到原来的页面并更新主页面,   利用tableview进行显示通讯录列表,当点击某一个好友时,可以进入到详情进行编辑修改名称,然后把修改后的名称在显示到tableview 上面,当点击增加好友时可进入添加好友页面   ...
继续阅读 »
用到iOS中委托知识点把修改后的值传到原来的页面并更新主页面,
 
利用tableview进行显示通讯录列表,当点击某一个好友时,可以进入到详情进行编辑修改名称,然后把修改后的名称在显示到tableview 上面,当点击增加好友时可进入添加好友页面
 
 

收起阅读 »

一个基于公司招聘的小demo

iOS
用oc手工写的一个小的demo来实现的公司管理查询员工信息,输入公司的员工的名字就可以调出员工的个人信息,以及进行简历的筛选
用oc手工写的一个小的demo来实现的公司管理查询员工信息,输入公司的员工的名字就可以调出员工的个人信息,以及进行简历的筛选

好友列表展开、收起、点击修改、原理实现

好友列表,单独拉出来做了个文档,已上传。展开收起的实现其实是很简单的。首先我们把好友列表写在了UITableView 上,点击每个cell 可以触发 UITableViewDelegate 协议里面的 didSelectRowAtIndexPath:(NSIn...
继续阅读 »
好友列表,单独拉出来做了个文档,已上传。展开收起的实现其实是很简单的。首先我们把好友列表写在了UITableView 上,点击每个cell 可以触发 UITableViewDelegate 协议里面的 didSelectRowAtIndexPath:(NSIndexPath *)indexPath 这个协议方法跳转至下个界面查看并修改信息。而后在详情页即修改信息的控制器里面声明一个协议方法,-(void)editName:(NSString *)name andPhone:(NSString *)phone;  (可以再添加多个修改信息) [_delegate editName:nameText.text andPhone:phoneText.text]; // 实现修改     ,完成修改后,在委托者的控制器里面,这里是 ViewController,实现代理方法,并 [table reloadData]; // 刷新原数组显示。即可完成修改。同样在点击事件里,控制返回cell 行数,并 reloadData, 即可完成列表的展开、收起。 收起阅读 »

iOS集成支付宝

iOS
做了个文档传到附件了
做了个文档传到附件了

Ios上架流程

一、生成发布证书: 1、首先创建钥匙串配置文件,进入钥匙串 2、选择从证书颁发机构请求证书: 3、填写信息   4、保存配置信息 5、进入苹果开发者网页:https://d...
继续阅读 »
一、生成发布证书:

1、首先创建钥匙串配置文件,进入钥匙串


图片1.png


2、选择从证书颁发机构请求证书:


图片2.png



3、填写信息


图片3.png



 
4、保存配置信息


图片5.png


5、进入苹果开发者网页:https://developer.apple.com/
6、点击member center,如下图:


图片6.png


7、输入账号,进入开发者主页,并选择证书选项:


图片7.png


8、进入证书页,首先创建证书


图片8.png


9、选择production,并点击+

图片9.png


10、选择需要创建的证书类型,在这里我们选择production中的appstore and ad hoc,如果有推送功能,则重复以上步骤,选择第二个:


图片10.png


11、点击continu,直到进入Cenerate界面,点击choose file,上传之前创建好的


图片11.png


12、选择钥匙串授权文件,点击继续:


图片12.png


13、生成之后,我们会跳转到Download界面,点击界面中的“Download”下载下来,双击我们生成的.cer文件,一定要双击,双击后它会默认安装到钥匙串中。(推送证书重复之前步骤)
14、之后点击左边目录中的“Identifiers”下的“App IDs”,点击+:


图片14.png


15、填写创建证书所需要的信息(如果需要推送则选择第一个,固定标识):


图片15.png




图片16.png




图片17.png


16、点击continue,然后submit,如果有推送功能,则创建推送证书,创建推送证书过程中,会有个选择appids的选项,选择对应的appids:


图片18.png


17、注册手持设备,点击左边目录中的“Devices”,同样点击右上方的“十”号,进行添加。


图片19.png


18、输入设备名称以及udid,我们可以通过iTunes获取设备的udid:


图片20.png


19、点击continue,然后点击注册,完成设备的条件
20、创建Provisioning Profiles,也就是手机上使用的证书,点击最左边目录栏,选择“Provisioning Profiles”目录下的“All”,同样点击右上方的“十”号进入证书添加界面


图片21.png


21、选择APP Store,点击continue,选择我们之前创建的App ID,点击continue:


图片22.png


22、在下面选项里面选择我们之前生成的授权发布证书的名字,点击continue:


图片23.png


23、接下来输入证书名字,改名字将会在xcode中显示,然后点击Create来创建证书,最后下载,双击进行安装。
 

二、xcode打包发布的ipa
1、修改项目中TARGETS中的Bundle identifier,与我们之前创建的App ID中的标识保持一致:


图片24.png


2、分别设置TARGETS和PROJECT里面Build Settings中对应的Code Signing属性:


图片25.png


3、选择IOS Device,然后点击xcode菜单中的Product,选择Archive,进行打包:


图片26.png


4、打包完成后进入Archive界面,我们可以直接submit到appStore,也可以选择导出到本地,通过上传工具再上传:


图片27.png


5、点export,选择打包ipa的用途,我们选第一个,发布到appstore:


图片28.png


6、点击next,进入开发者账号的选择,如果之前设定好,会直接显示,反之则会提示输入账号和密码:


图片29.png


7、点击choose,会出现对应的应用信息,然后点击export,取个名字,直接保存即可
 

三、iTunes connect中创建app
1、进入苹果开发者网页:https://developer.apple.com/,进入member center,选择iTunes Connect:


图片30.png


3、点击+,选择新建IOS App,然后在弹出框填写自己的app信息:


图片31.png




图片32.png


4、点击创建,进入app详情页,上传自己的截图、app的展示图、app的描述等。
5:用application loader将ipa文件上传到iTunes connect,随后在app详情页的build处点击+,选择自己上传的ipa文件即可。
 
 
 
 
 
  收起阅读 »

iOS 通过 REST API 接口上传文件(图片)

官方文档中提到了通过通过 REST API 接口上传文件的问题。这里我简单介绍一下通过REST API 接口上传图片的问题。     我们知道由于iOS无法通过html表单来上传图片,因此想要上传图片,必须实现http请求,而不能像其他语言那样通过html表...
继续阅读 »


未命名.png


官方文档中提到了通过通过 REST API 接口上传文件的问题。这里我简单介绍一下通过REST API 接口上传图片的问题。
    我们知道由于iOS无法通过html表单来上传图片,因此想要上传图片,必须实现http请求,而不能像其他语言那样通过html表单的post就能上传。这个文档中貌似没有说清楚
上传图片的http post请求的格式是这样的:
    

11.png


第一行是指定了http post请求的编码方式为multipart/form-data(上传文件必须用这个)。 
boundary=AaB03x说明了AaB03x为分界线。比如 --AaB03x 就是一个分界线的意思 

content-disposition: form-data; name="field1" 

Hello Boris! 
 
 这句话声明了请求中的一个字段的名称,如field1  以及字段的值,如Hello Boris! 
这里类似form表单中的 
中间的空行是必须的。一般是文件的一些属性 

不同的字段之间用分界线分开,分界线需要单独一行,如 --AaB03x-- 

分界线的下一行,是下一个字段 

content-disposition: form-data; name="pic"; filename="boris.png" 
Content-Type: image/png 

... contents of boris.png ... 
--AaB03x-- 

这里声明了变量pic,也就是我们要传的文件,上传文件的时候需要在后边指定filename:filename="boris.png" 
并且需要在下一行指定文件的格式:Content-Type: image/png 

... contents of boris.png ...  这里是boris.png的二进制内容(也就是data类型),如 <89504e47 0d0a1a0a 0000000d 49484452 000000b4 000000b4 08020000 00b2af91 65000020 00494441 5478012c dd79b724 6b7616f6 8c888c88 8c9c8733 55ddb1d5 6a0db486 06218401 ...... 

在http post请求的结尾,需要有一个分界线,但是是前后都有--的:--AaB03x-- 

以上的这些格式,是http的规范,每个空行,空格都是必须的。 
 
下边是iOS的实现代码
/*---------------------------------上传图片-------------------------------------------*/

    //分界线的标识符
    NSString *TWITTERFON_FORM_BOUNDARY = @"AaB03x";
    //上传的接口
    NSURL *url = [NSURL URLWithString:@"https://a1.easemob.com/环信ID/APP名字/chatfiles"];
    //要上传的图片,得到data
    NSData *data = UIImagePNGRepresentation([UIImage imageNamed:@"defain"]);//这是我的本地图片
    //声明file字段,文件名为defain.png, 声明上传文件的格式
    NSString* strBodyBegin = [NSString stringWithFormat:@"--%@\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\nContent-Type: %@\n\n", TWITTERFON_FORM_BOUNDARY, @"file",  @"defain.png", @"image/png"];
    //声明结束符:--AaB03x--
    NSString* strBodyEnd = [NSString stringWithFormat:@"\n--%@--",TWITTERFON_FORM_BOUNDARY];
    NSMutableData *httpBody = [NSMutableData data];
    //表单开始
    [httpBody appendData:[strBodyBegin dataUsingEncoding:NSUTF8StringEncoding]];
    //填入数据
    [httpBody appendData:data];
    //表单结束
    [httpBody appendData:[strBodyEnd dataUsingEncoding:NSUTF8StringEncoding]]; 
/*
注意:这里我没有用下面的字段(它主要用来描述text格式的文本信息)
content-disposition: form-data; name="field1" 
Hello Boris! 
*/   
    NSMutableURLRequest* httpPutRequest = [[NSMutableURLRequest alloc] init];
    [httpPutRequest setURL:url];
    //设置请求方法
    [httpPutRequest setHTTPMethod:@"POST"];
    [httpPutRequest setTimeoutInterval: 60000];
    //设置HTTPHeader中token的值
    [httpPutRequest setValue:@"Bearer **************************************************" forHTTPHeaderField:@"Authorization"];
    //设置访问权限
    [httpPutRequest setValue:@"true" forHTTPHeaderField:@"restrict-access"];
    //设置Content-Length
    [httpPutRequest setValue:[NSString stringWithFormat:@"%@", @(httpBody.length)] forHTTPHeaderField:@"Content-Length"];
    //设置HTTPHeader中Content-Type的值
    [httpPutRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",TWITTERFON_FORM_BOUNDARY] forHTTPHeaderField:@"Content-Type"];
    //将表单添加到请求体
    httpPutRequest.HTTPBody = httpBody;
    //异步请求
    [NSURLConnection sendAsynchronousRequest:httpPutRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
       if (connectionError == nil) {
            id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
            NSLog(@"服务器返回信息%@",obj);
        }
    }];
 
//服务器返回信息
2016-01-03 14:30:20.272 Fuhome-App[1356:154571] 服务器返回信息{
    action = post;
    application = "************";
    applicationName = **********;
    duration = 81;
    entities =     (
                {
            "share-secret" = "*********";
            type = chatfile;
            uuid = "***********";
        }
    );
    organization = ******;
    path = "/chatfiles";
    timestamp = 1451802620175;
    uri = "https://a1.easemob.com/******/*******/chatfiles";
}
 

      
  收起阅读 »

关于demo3.0表情,文字混合输入问题

在上一个2.0版本中,我有一天脑洞大开,突然想试试,聊天的输入内容是如何删除的。于是我输入了一些文字,发现删除时确实是一个一个删除的,接着测试,我在输入文字的同时,输入了表情,demo自带的表情。然后删除,发现也没问题。那么继续,我在输入文字时,夹杂了系统中得...
继续阅读 »
在上一个2.0版本中,我有一天脑洞大开,突然想试试,聊天的输入内容是如何删除的。于是我输入了一些文字,发现删除时确实是一个一个删除的,接着测试,我在输入文字的同时,输入了表情,demo自带的表情。然后删除,发现也没问题。那么继续,我在输入文字时,夹杂了系统中得emoji表情。然后,哈哈哈,程序跪了。查看代码后,发现一个emoji表情占两个字符,而是不是emoji表情居然是通过是否包含在_defaultEmoji进行的判断,而_defaultEmoji只包含demo自己的30多个表情,果然demo就是demo,参考一下就好。造成奔溃的原因就是我们输入了不属于_defaultEmoji的表情,在进行删除时,没有按两个字符进行删除。
#pragma mark - DXFaceDelegate

- (void)selectedFacialView:(NSString *)str isDelete:(BOOL)isDelete
{
NSString *chatText = self.inputTextView.text;

NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self.inputTextView.attributedText];

if (!isDelete && str.length > 0) {
NSRange range = [self.inputTextView selectedRange];
[attr insertAttributedString:[EaseEmotionEscape attStringFromTextForInputView:str] atIndex:range.location];
self.inputTextView.text = @"";
self.inputTextView.attributedText = attr;
// self.inputTextView.text = [NSString stringWithFormat:@"%@%@",chatText,str];
}
else {
if (chatText.length > 0) {
NSInteger length = 1;
if (chatText.length >= 2) {
NSString *subStr = [chatText substringFromIndex:chatText.length-2];
if ([_defaultEmoji containsObject:subStr]) {
length = 2;
}
}
self.inputTextView.attributedText = [self backspaceText:attr length:length];
}
}

[self textViewDidChange:self.inputTextView];
}
解决也很简单,我再判断表情时,同时使用了下面的方法。这个方法我加在了NSString的扩展中。
+ (BOOL)stringContainsEmoji:(NSString *)string
{
__block BOOL returnValue = NO;

[string enumerateSubstringsInRange:NSMakeRange(0, [string length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
const unichar hs = [substring characterAtIndex:0];
if (0xd800 <= hs && hs <= 0xdbff) {
if (substring.length > 1) {
const unichar ls = [substring characterAtIndex:1];
const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc && uc <= 0x1f77f) {
returnValue = YES;
}
}
} else if (substring.length > 1) {
const unichar ls = [substring characterAtIndex:1];
if (ls == 0x20e3) {
returnValue = YES;
}
} else {
if (0x2100 <= hs && hs <= 0x27ff) {
returnValue = YES;
} else if (0x2B05 <= hs && hs <= 0x2b07) {
returnValue = YES;
} else if (0x2934 <= hs && hs <= 0x2935) {
returnValue = YES;
} else if (0x3297 <= hs && hs <= 0x3299) {
returnValue = YES;
} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) {
returnValue = YES;
}
}
}];

return returnValue;
}
注意:下面的代码为更新版本,上面的代码还是太久远了,下面的也是网上找到的,感觉还比较新。
+ (void)load {

    VariationSelectors = [NSCharacterSet characterSetWithRange:NSMakeRange(0xFE00, 16)];

}




- (BOOL)isEmoji {

    if ([self rangeOfCharacterFromSet: VariationSelectors].location != NSNotFound) {

        return YES;

    }

    

    const unichar high = [self characterAtIndex: 0];

    

    // Surrogate pair (U+1D000-1F9FF)

    if (0xD800 <= high && high <= 0xDBFF) {

        const unichar low = [self characterAtIndex: 1];

        const int codepoint = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;

        

        return (0x1D000 <= codepoint && codepoint <= 0x1F9FF);

        

        // Not surrogate pair (U+2100-27BF)

    } else {

        return (0x2100 <= high && high <= 0x27BF);

    }

}
收起阅读 »

如何通过REST接口获取token

/*--------------------------获取管理员oken---------------------------------------*/      NSURL *url = [NSURL URLWithString:@"https://a...
继续阅读 »
/*--------------------------获取管理员oken---------------------------------------*/
     NSURL *url = [NSURL URLWithString:@"https://a1.easemob.com/企业ID/应用app名称/token"];
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     request.HTTPMethod = @"POST";
     NSDictionary *body = @{@"grant_type":@"client_credentials",@"client_id": @"填写Client Id",@"client_secret":@"填写Client Secret"};
     [request setHTTPBody:[body JSONData]];//JSONData为一个库的方法,该库在下面附件中
     [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
     //连接,异步
     [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
     if (connectionError == nil) {
     id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
     NSLog(@"服务器返回信息%@",obj);
     NSString* token = [obj objectForKey:@"access_token"];
     if(token)
     {
     //     access_token 保存一下(可以把token保存到本地)
     [[NSUserDefaults standardUserDefaults] setObject:token forKey:@"access_token"];
     [[NSUserDefaults standardUserDefaults] synchronize];
     }
     }
     NSLog(@"错误信息%@",connectionError);
     }]; 收起阅读 »

【有奖征集】环信开发文档Feedback

为了更好地解决开发者在集成环信产品时遇到的问题,降低使用难度。 我们专门开设一个帖子征集开发文档的不足之处。请大家不吝指正,毫无保留的提出意见和建议。 开发文档的地址是:http://docs.easemob.com 不管是界面,内容,还是结构。任何跟文档...
继续阅读 »
为了更好地解决开发者在集成环信产品时遇到的问题,降低使用难度。
我们专门开设一个帖子征集开发文档的不足之处。请大家不吝指正,毫无保留的提出意见和建议。

开发文档的地址是:http://docs.easemob.com

不管是界面,内容,还是结构。任何跟文档站相关的统统可以吐槽。我们将统一收集整理,并不断改进我们的产品。

当然,这一切也许还会有意想不到的惊喜哦~ 我们将对有价值的回复提供现金赞赏!

这种主动求吐槽,还派发奖赏的玩法好像也就我们一家了吧~

GO! GO! GO! 收起阅读 »

推荐一款编程字体,让代码看着更美

    在IDE(集成开发环境)中进行程序开发时,字体的选择看上去并不是那么的重要。但是,一款优雅的字体在整体视觉上能让代码看上去更美,营造一个积极的气氛,眼睛不再那么累,心情就会显得不错,那么你的开发效率自然也会有所提升。   今天,就给大家推荐一款字体,一...
继续阅读 »
 
 
在IDE(集成开发环境)中进行程序开发时,字体的选择看上去并不是那么的重要。但是,一款优雅的字体在整体视觉上能让代码看上去更美,营造一个积极的气氛,眼睛不再那么累,心情就会显得不错,那么你的开发效率自然也会有所提升。
 
今天,就给大家推荐一款字体,一款美美的字体,也是个人长久使用的字体。在这之前,先简单介绍一下Eclipse的默认字体 —— Consolas。既然被Eclipse选为默认字体,Consolas必然已经得到了广大编程人员的认可,优势之处无需多言,但美中不足的是,Consolas显示的中文比英文要小,如图所示:


1094967-49c4561e6be8713c.jpg


通常,为了便于操作,我们总是希望在PC一屏中显示更多的代码,于是便采取缩小字号的方式。但是,在Eclipse默认字体下,中文汉字、中文符号等就显得特别小以至于很难看清,眼睛非常之累。

相比之下,本文中的主角要出现了 —— YaHei.Consolas.1.12.ttf 。这是一款集成了微软雅黑的Consolas,非常漂亮,具体效果,有图有真相:


1094967-e0dbdbee5b20d42d.jpg


是不是有一种焕然一新的感觉呢?是不是显得特别大气呢?接下来就看一下在Eclipse中的安装教程吧。

首选下载 YaHei.Consolas.1.12.ttf 字体并复制到电脑控制面板中的字体目录下,Google Code 官方下载地址(翻墙):

https://code.google.com/p/uigroupcode/downloads/detail?name=YaHei.Consolas.1.12.zip&can=2&q=

CSDN资源下载地址:

http://download.csdn.net/detail/wenbitianxiafeng/9384674

在Eclipse中,选择[Windows] --> [Preference] --> [General] --> [appearance] --> [Colors and Fonts] --> [Basic] --> [Text Font],进入字体编辑窗口:

1094967-9c2edd5670821939.jpg


点击 [Edit],可以看到Eclipse默认的是Consolas字体:

1094967-b558b6e6c763601d.jpg


在字体列表中选中 YaHei.Consolas.1.12.ttf ,确定并应用即可。

然后就可以尽情的享受这款字体带来的愉悦吧。

PS:如果大家有更好的编程字体,欢迎评论推荐~ 收起阅读 »

ios详细的证书创建和上传

为新手制定一个详细的创建推送证书流程 第一步:生成.certSigningRequest证书 打开钥匙串,在钥匙串导航栏找到,“钥匙串访问”这一项,在钥匙串访问找到“从证书颁发机构请求证书”这一项,如图 点击后进入到如图界面 用...
继续阅读 »


为新手制定一个详细的创建推送证书流程
第一步:生成.certSigningRequest证书
打开钥匙串,在钥匙串导航栏找到,“钥匙串访问”这一项,在钥匙串访问找到“从证书颁发机构请求证书”这一项,如图

p1.png


点击后进入到如图界面

p2.png


用户电子邮件地址:写上你的邮件就行
常用名称:随便写
然后选择储存到磁盘,CA电子邮件地址不用写,点继续就生成了.certSigningRequest文件了,记得这个文件的储存位置,一会我们生成推送证书还要用到
第二步:创建推送证书
打开开发者中心,进入生成证书界面(应该找的到吧),进入之后,如果你要生成测试环境的推送证书,选择Certificates-Development,如果你要生成正式环境证书就选择Certificates-Priduction,这里我们以测试为例,点击右上角加号,如图

p3.png


进入下个界面,如果你要生成测试环境推送证书,就选择红色部分,你要生成正式环境证书,就选择绿色部分,如图

p4.png


点Continue,进入下个界面,让选择app id,提醒一下,选择app id要和你工程的Bundle Identifier对应,新手应该找的到Bundle Identifier吧,找不到看下面的图,我截了张图

p10.png


选择完app id ,一直点击Continue,进入如图界面

p5.png


让选择文件,还记得我们刚开始用钥匙串生成的.certSigningRequest在哪放着吧,就选择它就对了,选择完之后,接着往下面走,把证书下载下来之后,双击就放到钥匙串里面了,这时候推送证书就生成完了
第三步:生成p12文件
我们最终放到环信后台的证书是p12证书,打开钥匙串,找到我们刚才生成的证书,右键找到导出选项,如图

p6.png


点击导出“Apple Development.....”之后进入下个界面,如图:

p7.png


红色圈着的部分,储存为:里面要写上证书的名字,名字随便起,不过要记住这个名字,一会在环信后台上传证书还要用到这个名字,名字填完之后,点击储存,储存的位置要记住,一会还要用这个文件,这个文件就是我们要上传到环信的后台的p12文件,(储存的时候会让你输入密码,这个密码你要记住,一会上传到环信后台证书的时候要用到这个密码),这时候p12文件就制作完成了。
第四步:把p12文件上传到环信后台
打开我们的环信后台,找到 推送证书-ios,下面有上传证书的地方,如图

p8.png


证书名称:就是我们刚才生成p12的名称,我的叫zhuma_test
证书:就是我们刚才生成的p12证书,找到传上去
证书密码:生成p12证书的时候输入的密码(不是瞎输的吧,能记得吗,记不得重新导p12文件吧)
证书类型:我生成的是开发环境的证书,就勾选开发环境
填完之后就可以上传了,上传成功,就ok了 收起阅读 »

环信移动客服v4.2产品更新说明

环信移动客服v4.2 产品更新说明   新增功能   【客服系统】 多级会话标签 【数据统计】 聊天消息导出 【客服系统】 客服头像和企业logo 【客服系统】 访客超时未回复自动结束会话 【客服系统】 待接入筛选和搜索 【...
继续阅读 »
环信移动客服v4.2
产品更新说明
 

QQ截图20151229144613.png



新增功能
 
【客服系统】 多级会话标签
【数据统计】 聊天消息导出
【客服系统】 客服头像和企业logo
【客服系统】 访客超时未回复自动结束会话
【客服系统】 待接入筛选和搜索
【客服系统】 微博集成
【web插件】 访客端web插件开发者版
【客服系统】 会话显示关联名称
【客服系统】 自定义事件推送
【客服系统】 访客统计
【客服系统】 Android手机客服工作台新功能同步   
 
优化功能    
 
【web插件】 客服端增加ctrv+v图片预览
【客服系统】 欢迎语长度增加
【客服系统】 客服模式支持菜单收起
【数据统计】 历史会话支持模糊查询
【数据统计】 历史会话增加渠道、关联、评分等查询维度
【客服系统】 增加“系统开关”设置面板,统一管理系统开关
【客服系统】 优化文案
 
进入体验最新功能吧:  http://www.easemob.com/services

  收起阅读 »

在较成熟的中大型公司做创业项目,是种什么样的体验?

在体制发展较成熟的中大型公司里,搞创业项目,也是创业的一种姿态。     记得之前经常会碰到一些即将毕业的或者面临择业的朋友,会问到“依照现在国内互联网公司发展的情况,现在选择去一些较成熟的公司,如BAT这类的公司更好呢,还是选择去一些创业公司更好呢?”   ...
继续阅读 »
在体制发展较成熟的中大型公司里,搞创业项目,也是创业的一种姿态。  
 
记得之前经常会碰到一些即将毕业的或者面临择业的朋友,会问到“依照现在国内互联网公司发展的情况,现在选择去一些较成熟的公司,如BAT这类的公司更好呢,还是选择去一些创业公司更好呢?” 
 

                                   

banner_sample.png


 
 
很多专业的职业规划指导师,都会列出N条这样选择或者那样选择的优缺点,但是今天我想和大家分享另外一种工作的姿态,如标题所示。
 
什么样的公司称得上较成熟的中大型公司呢?这个当然也没有统一标准,不过按照目前国内互联网公司的现状来说的话,除了以BAT为首的大佬们,很多二三线的公司也称得上中大型公司,如网易、携程、美团、赶集网、新浪、搜狐等等;除了互联网行业,每个行业也有其龙头标杆,一般排在行业前5的公司,也可以算我要说的这种中大型企业;另外公司员工也可以作为参照,员工至少也是千人以上的级别。 
 
我所在的公司的是一家传统做软件外包服务的公司,员工近2000人,公司成立十年,业务发展稳定,在行业中也排的上前几。 
 
只是在这两年互联网发展如此迅猛的情况下,公司高层也一直谋划转型,希望做试水一些互联网的项目,因此我们的项目也在这种的情况下孵化了。 
 
我们产品叫摘客,是一款主推个性化推荐的互联网资讯聚合阅读产品,目前除了网站,iOS和Android版本的App也都已经上线。
 
摘客官网
 
言归正传,在这样的公司背景下,做这样一个未知的项目,体验可以分为以下几点: 
 
 1)生存问题没创业公司那么严峻 这一点可能也是唯一的好处啊,但它却是重要和必须的。显而易见,不太需要担心钱用完了,明天公司倒闭了这样的问题,因此在选择做与不做上,没有太多梦想与面包的挣扎; 
 
2)公司少数支持,资源匮乏 除了以上一点,貌似剩下的都是呕心沥血啊。大公司发展到这个地步,自然有它成熟的机制和业务发展模式,每个部门、岗位都仍然在它正轨上运行着;而你这样一样不合群新生的项目,自然在没有关系利益的时候,很难获得其他资源部门的支持;比如我们在APP,做网站的时候,需要界面UI设计,而我们核心的项目组成员保持为核心后台的开发,因此需要找我们公司UED项目组来合作;但是由于我们目前并不盈利,而高层也不可能来经常盯着这些事情,因此在和不同部门沟通协作的时候, 通常被作为优先级不高的、要求不高的事项,设计师自然也不会卖力做设计,这样的出品自然劳心劳力啊,吐槽一百遍~ 
 
3)没有经验积累 既然是创业项目,当然是和公司原先的业务发展不同,可想而知,公司在这项目方面的积累也是少之又少的。非常明显的一点,就是互联网产品的运营。由于公司之前都做的都是软件外包,公司的市场部也只要是针对2b的模式来做,完全不懂2C的玩法和运营。因此在完成产品的同时,运营的工作也成了我们项目的一大难题,由于没有经验可带队的人,我们运营都是从头开始,一路摸索而来,现在仍在继续抹黑爬行~~继续吐血~ 
 
4)发挥空间相对更多 相较于企业的传统业务和项目,创业型的项目在公司更有发挥空间。因为传统业务已经有较成熟的模式,分工细致,岗位明确,因此对于个人而言,在常规业务中想要争取向上层升职,也是较困难的。而在新兴项目中,基本都是空白,较容易成为第一个吃螃蟹的人。 
 
先写这些啦,其实在不同状态、环境下,都有来自各方无形的压力,写这些希望能分享给大家,纯属个人体验,欢迎交流分享~
 
下面分享我们产品的链接

                                   

最新二维码下载.png


 
  收起阅读 »

感恩有你,Merry Christmas

  因为有你,环信连接人与人才有价值。 因为有你,环信连接人与商业才有基础。 感谢有你,环信飞速发展,一年四轮融资。 感谢有你,环信即时通讯云稳居行业第一,移动客服领军行业。 感谢有你,环信大数据产品,反垃圾服务等如雨后春笋。 感谢有你,感恩有你,感激有你,“...
继续阅读 »
 
因为有你,环信连接人与人才有价值。
因为有你,环信连接人与商业才有基础。
感谢有你,环信飞速发展,一年四轮融资。
感谢有你,环信即时通讯云稳居行业第一,移动客服领军行业。
感谢有你,环信大数据产品,反垃圾服务等如雨后春笋。
感谢有你,感恩有你,感激有你,“环信”有“你”而精彩!
 
 
Merry Christmas
真诚的祝你圣诞快乐!

J{(}BNF76ZK0LDUV06X0R4M.jpg

收起阅读 »

【新特性】移动客服上线自定义富媒体消息的扩展功能

移动客服这边实现了自定义富媒体消息的扩展功能,允许客户为自己的客服坐席提供自 定义的包含图片,文字,链接等丰富内容的消息,用来向APP用户发送精美的产品介 绍,订单信息等等   简介 环信自定义消息API允许客户在坐席工作台加载自己的消息编辑页面,按照环信自定...
继续阅读 »
移动客服这边实现了自定义富媒体消息的扩展功能,允许客户为自己的客服坐席提供自
定义的包含图片,文字,链接等丰富内容的消息,用来向APP用户发送精美的产品介
绍,订单信息等等
 
简介
环信自定义消息API允许客户在坐席工作台加载自己的消息编辑页面,按照环信自定义消息的格式发送包含图文和链接的消息给访客,从而实现从坐席到访客的产品介绍,订单消息等丰富内容的可扩展消息。
 
使用场景:
开通自定义消息功能后,在消息编辑窗上方会增加自定义消息按钮:

图片1.png


 

点击自定义消息按钮,会弹出iFrame窗口并加载客户在开通功能时提供的的消息编辑页面

图片2.png


 

在编辑页面勾选消息,并确认发送后,消息会直接发送给访客,并显示在客服坐席的聊天记录窗口中。

图片3.png




图片4.png



 
访客APP一侧按照约定的消息格式做展示
 
与客服对话中的自定义消息:

图片5.png


 

点击打开后的自定义消息内容:

图片6.png


 

多个产品组合的购物清单:

图片7.png



新特性开通请联系“在线技术支持
新特性体验请点击“环信移动客服收起阅读 »

注册环信

我认为注册有两种方案,分别对应上图。 1.预注册一批账号备用。 这个方式主要是服务器端先注册一定数量的环信账号放到号码池,这样当用户注册时,可以直接从号码池分配,当号码池中的号码不足一定数量时,服务器再去注册,这样将用户中注册的流程分成了异步,提高了注册效率。...
继续阅读 »

注册账号.jpg


我认为注册有两种方案,分别对应上图。
1.预注册一批账号备用。
这个方式主要是服务器端先注册一定数量的环信账号放到号码池,这样当用户注册时,可以直接从号码池分配,当号码池中的号码不足一定数量时,服务器再去注册,这样将用户中注册的流程分成了异步,提高了注册效率。
2.直接注册。
这种方式就是当用户注册我的app时,我的服务器同步向环信注册。这样会增加客户端返回时间。
  收起阅读 »