https://www.jianshu.com/p/2bfb1c4e9f21

前言

公司业务需要,PC端,移动端都用到了第三方 网易云信 IM来实现在线客服咨询。
在这当中难免遇到一些需求是网易云信没有提供,需要自行编码进行扩展的。写此篇文章的目的正是因业务需要,需要在网易云信的基础上进行消息类型的扩展。

此篇文章里的代码是基于 网易云信 NIM_iOS_Demo_v4.5.0 版进行修改的

如下图所示的消息类型

 
带图片和文字,并且可点击的消息类型,(注意收到的消息和发送的消息文本颜色不一样)

标题是iOS版,可想而知,肯定还有其他如 Android版,Web版等,不可能此类型的消息(我称它为图文消息)只支持iOS,而在Android或Web端无法显示问题。以下附上其他版本扩展的链接

正文

  1. 下载demo后,双击 NIMDemo/NIM.xcworkspace 打开项目,然后运行,确保下载下来的demo能正确运行起来。

  2. 运行没有问题后,修改以下几个文件配置,将demo修改为自己所用。

    • 修改 Classes/Util/NTESDemoConfig.m中的_appKey,填入自己的appKey
- (instancetype)init
{
if (self = [super init])
{
_appKey = @"填入自己的appKey";
_apiURL = @"https://app.netease.im/api";
_apnsCername = @"ENTERPRISE";
_pkCername = @"DEMO_PUSH_KIT"; _redPacketConfig = [[NTESRedPacketConfig alloc] init];
}
return self;
}
  • 修改
- (NSString *)tokenByPassword
{
//demo直接使用username作为account,md5(password)作为token
//接入应用开发需要根据自己的实际情况来获取 account和token
//return [[NIMSDK sharedSDK] isUsingDemoAppKey] ? [self MD5String] : self;
return [self MD5String];
}

修改上述代码后,重新运行,即可使用自己的账号密码登录了。

  1. 添加测试发送图文链接的按钮,点击即发送图文链接消息

编辑NTESCellLayoutConfig.m文件,在init函数中 _types 增加一条

- (instancetype)init
{
if (self = [super init])
{
_types = @[
@"NTESJanKenPonAttachment",
@"NTESSnapchatAttachment",
@"NTESChartletAttachment",
@"NTESWhiteboardAttachment",
@"NTESRedPacketAttachment",
@"NTESRedPacketTipAttachment",
// 添加图文链接消息
@"NTESLinkAttachment"
];
_sessionCustomconfig = [[NTESSessionCustomContentConfig alloc] init];
_chatroomTextConfig = [[NTESChatroomTextContentConfig alloc] init];
_chatroomRobotConfig = [[NTESChatroomRobotContentConfig alloc] init];
}
return self;
}

编辑 NTESCustomAttachmentDecoder.m文件,checkAttachment函数中添加如下代码

//头部导入
#import "NTESLinkAttachment.h"
//... - (id<NIMCustomAttachment>)decodeAttachment:(NSString *)content
{
id<NIMCustomAttachment> attachment = nil; NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
if (data) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
if ([dict isKindOfClass:[NSDictionary class]])
{
NSInteger type = [dict jsonInteger:CMType];
NSDictionary *data = [dict jsonDict:CMData];
switch (type) {
//...
// 添加图文链接 case
case CustomMessageTypeLink:
{
attachment = [[NTESLinkAttachment alloc] init];
((NTESLinkAttachment *)attachment).title = [data jsonString:CMLinkPacketTitle];
((NTESLinkAttachment *)attachment).linkUrl = [data jsonString:CMLinkPacketLinkUrl];
((NTESLinkAttachment *)attachment).imageUrl = [data jsonString:CMLinkPacketImageUrl];
((NTESLinkAttachment *)attachment).describe = [data jsonString:CMLinkPacketDescribe];
}
break;
default:
break;
}
attachment = [self checkAttachment:attachment] ? attachment : nil;
}
}
return attachment;
} - (BOOL)checkAttachment:(id<NIMCustomAttachment>)attachment
{ // ... 省略前面的 if else if 块 // 添加如下代码
else if ([attachment isKindOfClass:[NTESLinkAttachment class]])
{
check = YES;
}
return check;
}

编辑NTESSessionConfig.m文件,在mediaItems函数中添加如下代码

//...
// 添加图文链接测试按钮,此处的 onTapMediaItemLinkPacket 在
NTESSessionViewController.m 中添加
NIMMediaItem *linkPacket = [NIMMediaItem item:@"onTapMediaItemLinkPacket:"
normalImage:[UIImage imageNamed:@"icon_redpacket_normal"]
selectedImage:[UIImage imageNamed:@"icon_redpacket_pressed"]
title:@"图文链接"];
//...
if (isMe)
{
items = @[janKenPon,fileTrans,tip];
}
else if(_session.sessionType == NIMSessionTypeTeam)
{
// 在群组消息里添加
items = @[janKenPon,teamMeeting,fileTrans,tip,redPacket,linkPacket];
}
else
{
// 添加图文链接测试按钮
items = @[janKenPon,audioChat,videoChat,fileTrans,snapChat,whiteBoard,tip,redPacket,linkPacket];
}

在 Classes/Sections/Session/Object/Attach目录下创建 NTESLinkAttachment文件,继承 NSObject类,实现 NIMCustomAttachment,NTESCustomAttachmentInfo协议

 
    创建Cocoa Touch Class文件 NTESLinkAttachment,命名规则尽量遵循云信命名规则

创建完成后,添加响应的属性值 标题title,跳转的链接linkUrl,图片imageUrl,描述describe

NTESLinkAttachment.h文件内容如下

#import <Foundation/Foundation.h>
#import "NTESCustomAttachmentDefines.h" @interface NTESLinkAttachment : NSObject<NIMCustomAttachment,NTESCustomAttachmentInfo> // 标题
@property (nonatomic, copy) NSString *title; // 点击跳转的链接地址
@property (nonatomic, copy) NSString *linkUrl; // 图片
@property (nonatomic, copy) NSString *imageUrl; // 描述
@property (nonatomic, copy) NSString *describe; @end

NTESLinkAttachment.m文件内容如下

复制之后,会有报错如 NTESSessionLinkContentView.h找不到,和 CMLinkPacket***未定义等相关错误,先别急,后面会讲到,如果看不顺眼可以先注释掉,回头再过来放开注释也行。(ps:本人非iOS开发,所以代码部分不做详细讲解)

#import "NTESLinkAttachment.h"
#import "NTESSessionLinkContentView.h" @implementation NTESLinkAttachment - (NSString *)encodeAttachment
{
NSDictionary *dict = @{
CMType : @(CustomMessageTypeRedPacket),
CMData : @{
CMLinkPacketTitle : self.title,
CMLinkPacketLinkUrl : self.linkUrl,
CMLinkPacketImageUrl : self.imageUrl,
CMLinkPacketDescribe : self.describe
}
};
NSData *data = [NSJSONSerialization dataWithJSONObject:dict
options:0
error:nil];
NSString *content = nil;
if (data) {
content = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
}
return content;
} - (NSString *)cellContent:(NIMMessage *)message{
return @"NTESSessionLinkContentView";
} - (CGSize)contentSize:(NIMMessage *)message cellWidth:(CGFloat)width{
CGFloat w = 240.0f;
CGFloat h = 40.0f;
CGFloat padding = 3.0f * 3;
if (self.imageUrl != nil) {
h += 140.f;
}
if (self.describe != nil) {
UIFont *font = [UIFont systemFontOfSize:12.0];
CGFloat height = [NTESSessionLinkContentView getHeightByWidth:w - padding title:self.describe font:font];
h += height + padding;
} return CGSizeMake(w, h);
} - (UIEdgeInsets)contentViewInsets:(NIMMessage *)message
{
CGFloat bubblePaddingForImage = 3.f;
CGFloat bubbleArrowWidthForImage = 5.f;
if (message.isOutgoingMsg) {
return UIEdgeInsetsMake(bubblePaddingForImage,bubblePaddingForImage,bubblePaddingForImage,bubblePaddingForImage + bubbleArrowWidthForImage);
}else{
return UIEdgeInsetsMake(bubblePaddingForImage,bubblePaddingForImage + bubbleArrowWidthForImage, bubblePaddingForImage,bubblePaddingForImage);
}
} - (BOOL)canBeRevoked
{
return YES;
} - (BOOL)canBeForwarded
{
return YES;
} @end

现在再来补充上面缺失的部分。
在 NTESCustomAttachmentDefines.h文件中定义如下四个字段。打开这个文件可以看到这个里面还定义了一些其他消息需要用到的字段,所以遵循人家的游戏规则,也在此处定义。

//...省略

typedef NS_ENUM(NSInteger,NTESCustomMessageType){
CustomMessageTypeJanKenPon = 1, //剪子石头布
CustomMessageTypeSnapchat = 2, //阅后即焚
CustomMessageTypeChartlet = 3, //贴图表情
CustomMessageTypeWhiteboard = 4, //白板会话
// (由于我其他平台图文消息type是5,刚好我们业务不需要发红包功能,这里我只好把5变成我的图文消息,把红包类型的消息去除)
CustomMessageTypeRedPacket = 5, //红包消息
CustomMessageTypeRedPacketTip = 6, //红包提示消息
}; //...省略 //红包
#define CMRedPacketTitle @"title" //红包标题
#define CMRedPacketContent @"content" //红包内容
#define CMRedPacketId @"redPacketId" //红包ID
//红包详情
#define CMRedPacketSendId @"sendPacketId"
#define CMRedPacketOpenId @"openPacketId"
#define CMRedPacketDone @"isGetDone"
// 添加此处四个字段用于图文链接消息使用
#define CMLinkPacketTitle @"title" //标题
#define CMLinkPacketLinkUrl @"link_url" //跳转链接
#define CMLinkPacketImageUrl @"image_url" //图片链接
#define CMLinkPacketDescribe @"describe" //描述
//...省略

Classes/Sections/Session/View/SessionCell/SessionContentView目录下创建Cocoach Touch Class文件 NIMSessionMessageContentView,此文件主要用来做图文链接消息的显示。

NIMSessionMessageContentView.h文件内容如下

#import "NIMSessionMessageContentView.h"

static NSString *const NIMDemoEventNameLinkingPacket = @"NIMDemoEventNameLinkingPacket";

@interface NTESSessionLinkContentView : NIMSessionMessageContentView

// 根据宽度,字体和文本内容获取高度
+ (CGFloat)getHeightByWidth:(CGFloat)width title:(NSString *)title font:(UIFont *)font; @end

NIMSessionMessageContentView.m文件内容如下

#import "NTESSessionLinkContentView.h"
#import "UIView+NTES.h"
#import "NTESLinkAttachment.h"
#import "NTESSessionUtil.h"
#import "UIImageView+WebCache.h" CGFloat titleHeight = 40.f; // title高度
CGFloat imageHeight = 120.f;// 图片高度 @interface NTESSessionLinkContentView() // 图文链接消息附件
@property (nonatomic,strong) NTESLinkAttachment *attachment; @property (nonatomic,strong) UILabel *titleLabel; @property (nonatomic,strong) UIImageView *imageView; @property (nonatomic,strong) UILabel *describeLabel; @end @implementation NTESSessionLinkContentView - (instancetype)initSessionMessageContentView{
self = [super initSessionMessageContentView];
if (self) {
self.opaque = YES; _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
_describeLabel = [[UILabel alloc] initWithFrame:CGRectZero];
}
return self;
} - (void)refresh:(NIMMessageModel *)data
{
[super refresh:data];
NIMCustomObject *customObject = (NIMCustomObject*)data.message.messageObject;
id attach = customObject.attachment; if ([attach isKindOfClass:[NTESLinkAttachment class]]) {
self.attachment = (NTESLinkAttachment *)attach; self.titleLabel.text = self.attachment.title;
[self addSubview:_titleLabel]; if (self.attachment.imageUrl != nil) {
NSURL *url = [NSURL URLWithString:self.attachment.imageUrl];
// 默认图片 default_image,记得在 Images.xcassets 中添加
[self.imageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"default_image"]];
[self.imageView sizeToFit];
[self addSubview:_imageView];
}
if (self.attachment.describe != nil) {
self.describeLabel.text = self.attachment.describe;
[self addSubview:_describeLabel];
}
}
} - (void)layoutSubviews{
[super layoutSubviews];
BOOL outgoing = self.model.message.isOutgoingMsg; UIEdgeInsets contentInsets = self.model.contentViewInsets;
CGSize contentSize = [self.model contentSize:self.superview.width];
CGFloat padding = 15; self.titleLabel.frame = CGRectMake(padding, contentInsets.left, contentSize.width - padding, titleHeight);
self.titleLabel.font = [UIFont systemFontOfSize:14.0];
self.titleLabel.numberOfLines = 1; // 详情描述距离
CGFloat describeY = titleHeight; if (self.attachment != nil && self.attachment.imageUrl != nil) {
self.imageView.frame = CGRectMake(
contentInsets.left + contentInsets.right,
titleHeight + contentInsets.top + 5,
contentSize.width - (contentInsets.left + contentInsets.right), imageHeight);
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
[self setBorderWithImageView:self.imageView top:TRUE left:FALSE bottom:TRUE right:FALSE borderColor:[UIColor lightGrayColor] borderWidth:0.3f];
describeY += imageHeight + contentInsets.top * 3 + 5 ;
} if (self.attachment != nil && self.attachment.describe != nil) {
UIFont *font = [UIFont systemFontOfSize:12.0];
self.describeLabel.font = font;
self.describeLabel.numberOfLines = 3;
CGFloat height = [NTESSessionLinkContentView getHeightByWidth:self.describeLabel.frame.size.width title:self.attachment.describe font:font];
self.describeLabel.frame = CGRectMake(padding, describeY, contentSize.width - padding, height + padding);
} // 发出去的消息
if (outgoing)
{
self.titleLabel.textColor = [UIColor whiteColor];
self.describeLabel.textColor = [UIColor whiteColor];
}
else
{
self.titleLabel.textColor = [UIColor blackColor];
self.describeLabel.textColor = [UIColor grayColor];
}
} // 根据宽动态获取高度
+ (CGFloat)getHeightByWidth:(CGFloat)width title:(NSString *)title font:(UIFont *)font
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, 0)];
label.text = title;
label.font = font;
label.numberOfLines = 0;
[label sizeToFit];
CGFloat height = label.frame.size.height;
return height;
} // 设置元素边框
-(void)setBorderWithImageView:(UIImageView *) imageView top:(BOOL)top left:(BOOL)left bottom:(BOOL)bottom right:(BOOL)right borderColor:(UIColor *)color borderWidth:(CGFloat)width
{
// 垂直内边距
CGFloat verticalPadding = 5.0f;
if (top)
{
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, -verticalPadding, imageView.frame.size.width, width);
layer.backgroundColor = color.CGColor;
[imageView.layer addSublayer:layer];
}
if (left)
{
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, width, imageView.frame.size.height);
layer.backgroundColor = color.CGColor;
[imageView.layer addSublayer:layer];
}
if (bottom)
{
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, imageView.frame.size.height - width + verticalPadding, imageView.frame.size.width, width);
layer.backgroundColor = color.CGColor;
[imageView.layer addSublayer:layer];
}
if (right)
{
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(imageView.frame.size.width - width, 0, width, imageView.frame.size.height);
layer.backgroundColor = color.CGColor;
[imageView.layer addSublayer:layer];
}
} - (void)onTouchUpInside:(id)sender
{
if ([self.delegate respondsToSelector:@selector(onCatchEvent:)]) {
NIMKitEvent *event = [[NIMKitEvent alloc] init];
event.eventName = NIMDemoEventNameLinkingPacket;
event.messageModel = self.model;
event.data = self;
[self.delegate onCatchEvent:event];
}
} @end

接下来我们添加图文按钮的点击事件处理。

 
下面代码添加此处按钮点击处理事件

打开文件 NTESSessionViewController.m, 编辑函数 onTapCell
在 if else if 代码块后面添加如下代码

// 头部需导入
#import "NTESLinkAttachment.h"
#import "NTESSessionLinkContentView.h"
#import "NTESWebViewController.h" // ... // 添加图文链接消息点击事件
else if ([eventName isEqualToString:NIMDemoEventNameLinkingPacket]) {
NIMCustomObject *object = event.messageModel.message.messageObject;
NTESLinkAttachment *attachment = (NTESLinkAttachment *)object.attachment;
[self onOpenWebView:attachment];
handled = YES;
}
// .... // 添加上面调用的 onOpenWebView 函数
- (void)onOpenWebView:(NTESLinkAttachment *)attachment {
// NTESWebViewController 是点击显示的图文消息后要跳转的页面,在构造函数添加跳转时传入 linkUrl
NTESWebViewController *vc = [[NTESWebViewController alloc] initWithUrl:attachment.linkUrl];
// 设置title
if (attachment && attachment.title != nil) {
vc.title = attachment.title;
}
[self.navigationController pushViewController:vc animated:YES];
} //...
#pragma mark - 图文链接
- (void)onTapMediaItemLinkPacket:(NIMMediaItem *)item
{
// 此处模拟测试数据
NTESLinkAttachment *attachment = [[NTESLinkAttachment alloc] init];
[attachment setTitle:@"暖冬季欢乐送"];
[attachment setLinkUrl:@"https://www.jianshu.com/u/bd57ade96e8a"];
[attachment setImageUrl:@"https://www.baidu.com/img/bd_logo1.png"];
[attachment setDescribe:@"家具满1000元减100元再返100元现金券!点击查看详情!"];
NIMMessage *message = [NTESSessionMsgConverter msgWithLink:attachment];
[self sendMessage:message];
}
//...

在目录 Classes/Sections/Session/ViewController添加上面使用到的 NTESWebViewController,用来显示点击后的网页
NTESWebViewController.h内容如下

#import <UIKit/UIKit.h>

@interface NTESWebViewController : UIViewController<UIWebViewDelegate>
{
UIWebView *webView;
} - (instancetype)initWithUrl:(NSString *)url; @end

NTESWebViewController.m内容如下

#import "NTESWebViewController.h"

@interface NTESWebViewController ()<UINavigationControllerDelegate>

@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator;
@property (nonatomic, strong) NSString *url; @end @implementation NTESWebViewController - (instancetype)initWithUrl:(NSString *)url
{
self = [super init];
if (self)
{
_url = url;
}
return self;
} - (void)viewDidLoad {
[super viewDidLoad]; // app 尺寸,去掉状态栏
CGRect mainScreen = [UIScreen mainScreen].applicationFrame;
// 1.创建webview,并设置大小
webView = [[UIWebView alloc] initWithFrame:CGRectMake(mainScreen.origin.x, mainScreen.origin.y, mainScreen.size.width, mainScreen.size.height)];
// 2.创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
// 3.加载网页
[webView loadRequest:request];
// 4.将webview添加到界面
[self.view addSubview:webView];
[webView setDelegate:self];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)webViewDidStartLoad:(UIWebView *)webView {
// 创建UIActivityIndicatorView背底半透明View
CGRect mainScreen = [UIScreen mainScreen].applicationFrame;
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(mainScreen.origin.x, mainScreen.origin.y, mainScreen.size.width, mainScreen.size.height)];
[view setTag:108];
[view setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:view]; self.activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
[self.activityIndicator setCenter:view.center];
[self.activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
[view addSubview:self.activityIndicator]; [self.activityIndicator startAnimating];
} - (void)webViewDidFinishLoad:(UIWebView *)webView {
[self.activityIndicator stopAnimating];
UIView *view = (UIView *)[self.view viewWithTag:108];
[view removeFromSuperview];
} - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
[self.activityIndicator stopAnimating];
UIView *view = (UIView *)[self.view viewWithTag:108];
[view removeFromSuperview];
} @end
  1. 添加显示自定义的图文消息
    上面第3个步骤其实已经做了大部分自定义的图文链接消息的显示工作了,此处添加图文链接消息的转换代码,
    编辑NTESSessionMsgConverter.h头文件
// ...
@class NTESLinkAttachment @interface NTESSessionMsgConverter : NSObject
// ...
// 添加链接消息
+ (NIMMessage *)msgWithLink:(NTESLinkAttachment *)attachment;
@end

在实现文件NTESSessionMsgConverter.m添加以下代码

//...
#import "NTESLinkAttachment.h" @implementation NTESSessionMsgConverter
//...
+ (NIMMessage *)msgWithLink:(NTESLinkAttachment *)attachment
{
NIMMessage *message = [[NIMMessage alloc] init];
NIMCustomObject *customObject = [[NIMCustomObject alloc] init];
customObject.attachment = attachment;
message.messageObject = customObject;
message.apnsContent = @"发来了链接信息";
return message;
} @end

5.修改消息列表中,显示的缩略文字

 
添加显示[图文链接]字样,如果不添加,默认显示的是[未知消息]

编辑 NTESSessionListViewController.m, 在contentForRecentSession中添加一条逻辑判断

// ...
#import "NTESLinkAttachment.h" // ...
- (NSAttributedString *)contentForRecentSession:(NIMRecentSession *)recent{
//...
else if ([object.attachment isKindOfClass:[NTESLinkAttachment class]]) {
text = @"[图文链接]";
} else {
text = @"[未知消息]";
}
//...
}
// ...

尾篇

到此,云信iOS端的扩展自定义消息已经完成。当然,这只是iOS的显示正常了,其他如web,Android,pc等客户端收到此类的消息,显示有问题,也是需要扩展调整的。此篇文章其他端的文章我会陆续更新,如果有需要的同学可以关注下。

以下附上其他版本扩展的链接

作者:醉生夢死
链接:https://www.jianshu.com/p/2bfb1c4e9f21
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

网易云信-新增自定义消息(iOS版)的更多相关文章

  1. 网易云信,发送验证码短信C#版代码

    网易云信发送短信代码(C# 版)....需要注意SHA1 String有转换小写!!!! using System; using System.Collections.Generic; using S ...

  2. 模板短信接口调用java,pythoy版(一) 网易云信

    说明 短信服务平台有很多,我只是个人需求,首次使用,算是测试用的,故选个网易(大公司). 稳定性:我只测试了15条短信... 不过前3条短信5分钟左右的延时,后面就比较快.... 我只是需要发短信,等 ...

  3. 网易新闻iOS版使用的18个开源组件

    转载来自:http://www.jianshu.com/p/8952944f7566  原文最后编辑时间:2015.05.19 网易新闻iOS版在开发过程中曾经使用过的第三方开源类库.组件 1.AFN ...

  4. 微信小程序开发中的二三事之网易云信IMSDK DEMO

    本文由作者邹永胜授权网易云社区发布. 简介 为了更好的展示我们即时通讯SDK强悍的能力,网易云信IM SDK微信小程序DEMO的开发就提上了日程.用产品的话说就是: 云信 IM 小程序 SDK 的能力 ...

  5. 【网易云信】H5 容器技术方案

    Native 开发原生应用是手机操作系统厂商(目前主要是苹果的 iOS 和 Google 的 Android)对外界提供的标准化的开发模式,他们对于 Native 开发提供了一套标准化实现和优化方案. ...

  6. 中国首个 SaaS 模式的云告警平台 iOS 版 APP 上线

    今天上午,国内首个 SaaS 模式的云告警平台 OneAlert 正式发布 ios 版 APP,每个 ios 用户,无需电脑,都可以通过手机全程跟踪所有告警,并且可以和每一个成员一键式电话沟通,团队协 ...

  7. 视频直播SDK-ios版

    IOS视频直播接入说明 一.名词解释 分辨率:用于计算机视频处理的图像,以水平和垂直方向上所能显示的像素数来表示分辨率.常见视频分辨率的有1080P即1920x1080,720P即1080x720,6 ...

  8. 子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践

    本文原文内容来自InfoQ的技术分享,本次有修订.勘误和加工,感谢原作者的分享. 1.前言 自从2018年8月20日子弹短信在锤子发布会露面之后(详见<老罗最新发布了“子弹短信”这款IM,主打熟 ...

  9. 通过微信Android和iOS版,看两大系统的差异

    由于设计师或者产品经理使用的移动设备大部分是iPhone,所以在做设计时,容易忽略Android和iOS的差异,按照自己的使用习惯进行设计,导致大部分设计师或产品经理做出的设计都是基于iOS规范或习惯 ...

随机推荐

  1. Idea 工具快捷合集

    官方下载地址 https://www.jetbrains.com/idea/download/#section=windows 商业版 与 社区版,商业版具有更多的功能 快捷一.修改 terminal ...

  2. C语言:假定输入的字符串只包含字母和*号,fun函数:除了尾部的*号以外,将字符的其他*号进行全部删除,形参p已经指向字符串中最后一个字母。-利用折半查找整数m在有序数组中的位置,若找到,返回下标值,否则返回-1。

    //假定输入的字符串只包含字母和*号,fun函数:除了尾部的*号以外,将字符的其他*号进行全部删除,形参p已经指向字符串中最后一个字母. #include <stdio.h> void f ...

  3. SpringBoot 系列

    https://my.oschina.net/xiedeshou?tab=newest&catalogId=5936801 SpringBoot | 第零章:前言 SpringBoot | 第 ...

  4. css 属性值 calc (目前只了解部分)

    移动端页面,有如下图的需求: 实现效果: 实现 css 代码: .list {/*父级*/ border: 1px solid #E9EAEA; border-radius: 2px; backgro ...

  5. 转载--php 7.2 安装 mcrypt 扩展

    在 php 官网下载 mcrypt 包,php 扩展官网 # wget http://pecl.php.net/get/mcrypt-1.0.1.tgz # tar xf mcrypt-1.0.1.t ...

  6. java月利率计算(等额本息贷款)

    等额本息 每月还款计算公式: 每月本息金额 = (本金×月利率×(1+月利率)^还款月数)÷ ((1+月利率)^还款月数-1)) 反转求出 月利率 月利率 如果根据上面公式反转是算不出来的. 下面给出 ...

  7. python数组冒号取值操作

    1.冒号的用法 1.1 一个冒号 a[i:j] 这里的i指起始位置,默认为0:j是终止位置,默认为len(a),在取出数组中的值时就会从数组下标i(包括)一直取到下标j(不包括j) 在一个冒号的情况下 ...

  8. WLC exclusionlist

    Configuring Client Exclusion Configuring Client Exclusion Policies (GUI) Step 1   Choose Security &g ...

  9. 【SSM】AppFileUtils

    11 package com.kikyo.sys.utils; import java.io.File; import java.io.IOException; import java.io.Inpu ...

  10. 时隔两天,三星再称GalaxyFold已准备就绪,王自如的脸还好吗?

    编辑 | 禾斗 出品 | 于见(mpyujian) 据消息人士透露,三星已经完成对其有缺陷的折叠智能手机进行了重新设计,Galaxy Fold准备适时再度推出,但问题是,作为消费者,我们准备好了吗? ...