add flying commit

This commit is contained in:
lianxiang
2018-08-28 17:00:43 +08:00
parent 6a8d55457a
commit 54e936535f
24 changed files with 1353 additions and 23 deletions
+5
View File
@@ -48,5 +48,10 @@
+(CGFloat)getSpaceLabelHeight:(NSString*)str withFont:(UIFont*)font maxSize:(CGSize)maxSize;
+(void)setLabelSpace:(UILabel*)label withValue:(NSString*)str withFont:(UIFont*)font;
/**
是否是手机号
*/
+(BOOL)isPhoneNumber:(NSString*)mobileNumber;
@end
+30
View File
@@ -144,4 +144,34 @@
label.attributedText = attributeStr;
}
+(BOOL)isPhoneNumber:(NSString*)mobileNumber{
if (mobileNumber.length != 11)
{
return NO;
}
/** * 手机号码: * 13[0-9], 14[5,7], 15[0, 1, 2, 3, 5, 6, 7, 8, 9], 17[6, 7, 8], 18[0-9], 170[0-9] * 移动号段: 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705 * 联通号段: 130,131,132,155,156,185,186,145,176,1709 * 电信号段: 133,153,180,181,189,177,1700 */
NSString *MOBILE = @"^1(3[0-9]|4[57]|5[0-35-9]|8[0-9]|7[0678])\\d{8}$";
/** * 中国移动:China Mobile * 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705 */
NSString *CM = @"(^1(3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(^1705\\d{7}$)";
/** * 中国联通:China Unicom * 130,131,132,155,156,185,186,145,176,1709 */
NSString *CU = @"(^1(3[0-2]|4[5]|5[56]|7[6]|8[56])\\d{8}$)|(^1709\\d{7}$)"; /** * 中国电信:China Telecom * 133,153,180,181,189,177,1700 */
NSString *CT = @"(^1(33|53|77|8[019])\\d{8}$)|(^1700\\d{7}$)";
/** 25 * 大陆地区固话及小灵通 26 * 区号:010,020,021,022,023,024,025,027,028,029 27 * 号码:七位或八位 28 */ //
// NSString * PHS = @"^(0[0-9]{2})\\d{8}$|^(0[0-9]{3}(\\d{7,8}))$";
NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
if (([regextestmobile evaluateWithObject:mobileNumber] == YES) || ([regextestcm evaluateWithObject:mobileNumber] == YES) || ([regextestct evaluateWithObject:mobileNumber] == YES) || ([regextestcu evaluateWithObject:mobileNumber] == YES)) {
return YES;
} else {
return NO;
}
}
@end
@@ -44,11 +44,10 @@
content.attachments = @[attachAudio];
//延迟通知 第一个参数是重复的时间间隔,最小60s,第二个参数是是否重复。
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:60 repeats:NO];
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timeInterval repeats:NO];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
GILog(@"成功添加推送");
}];
} else {
@@ -0,0 +1,34 @@
//
// LXDanMuManager.h
// LXAnimationTest
//
// Created by lianxiang on 2018/8/14.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface LXDanMuManager : NSObject
@property (nonatomic,strong) NSArray *trackSpeedArray;
@property (nonatomic,assign) float centerY;//所有轨道中心点
@property (nonatomic,assign) float trackWidth;
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic,strong) UIView *superView;
@property (nonatomic,strong) NSMutableArray *trackArray;
@property (nonatomic,strong) UIView *baseView;
+(LXDanMuManager *)shareInstance;
-(void)createDanmuWithTrackSpeedArray:(NSArray *)trackSpeedArray centerY:(float)centerY trackWidth:(float)trackWidth showInView:(UIView *)inView;
/**
插入一条弹幕,可以插入任意继承UIView的对象
@param customView 自定义的view(注意,最高不能超过轨道的高,否则会出问题)
@param trackIndex 插入弹幕的轨道(为-1则代表自动寻找最不拥挤的轨道插入)
*/
-(void)appendDanMuWithCustomView:(UIView *)customView toTrackIndex:(NSInteger)trackIndex;
-(void)start;
-(void)pause;
-(void)stop;
-(void)destory;
@end
+145
View File
@@ -0,0 +1,145 @@
//
// LXDanMuManager.m
// LXAnimationTest
//
// Created by lianxiang on 2018/8/14.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import "LXDanMuManager.h"
#import "LXDanMuTrackView.h"
@implementation LXDanMuManager
+ (LXDanMuManager *)shareInstance{
static LXDanMuManager *managerInstance = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
managerInstance = [[self alloc] init];
});
return managerInstance;
}
- (void)createDanmuWithTrackSpeedArray:(NSArray *)trackSpeedArray centerY:(float)centerY trackWidth:(float)trackWidth showInView:(UIView *)inView{
_superView = inView;
_trackSpeedArray = trackSpeedArray;
_centerY = centerY;
_trackWidth = trackWidth;
_trackArray = [[NSMutableArray alloc] init];
[self initUI];
}
-(void)initUI{
_baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,_trackWidth, 0)];
_baseView.userInteractionEnabled = NO;
[_superView addSubview:_baseView];
for (int i = 0; i<_trackSpeedArray.count; i++) {
LXDanMuTrackView *trackView = [[LXDanMuTrackView alloc] initWithFrame:CGRectMake(0, (TRACK_HEIGHT + TRACK_VERTICAL_PADDING) * i, _baseView.frame.size.width, TRACK_HEIGHT)];
[_baseView addSubview:trackView];
[_trackArray addObject:trackView];
if (_trackSpeedArray.count - 1 == i) {
_baseView.frame = CGRectMake(_baseView.frame.origin.x, _baseView.frame.origin.y, _baseView.frame.size.width,CGRectGetMaxY(trackView.frame));
_baseView.center = CGPointMake(_baseView.center.x, _centerY);
}
}
}
-(void)start{
if (_timer) {
return;
}
_timer = [NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(startAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
- (void)pause{
[_timer invalidate];
_timer = nil;
}
- (void)stop{
[_timer invalidate];
_timer = nil;
for (LXDanMuTrackView *trackView in _trackArray) {
[trackView cleanTrack];
}
}
- (void)destory{
[_timer invalidate];
_timer = nil;
for (LXDanMuTrackView * trackView in _trackArray) {
[trackView cleanTrack];
[trackView removeFromSuperview];
}
[_trackArray removeAllObjects];
[_baseView removeFromSuperview];
_trackSpeedArray = nil;
_superView = nil;
_baseView = nil;
_trackArray = nil;
}
-(void)startAction{
for (int i = 0; i < _trackArray.count; i++) {
//执行轨道定时器方法
LXDanMuTrackView *trackView = _trackArray[i];
[trackView moveViewWithSpeed:[_trackSpeedArray[i] floatValue]];
}
}
-(UIView *)baseView{
if (!_baseView) {
_baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,_trackWidth, 0)];
}
return _baseView;
}
//自定义的view(注意,最高不能超过轨道的高,会出问题)
- (void)appendDanMuWithCustomView:(UIView *)customView toTrackIndex:(NSInteger)trackIndex
{
//trackIndex 插入弹幕的轨道(为-1则代表自动寻找最不拥挤的轨道插入)
NSInteger realTrackIndex = trackIndex;
if (realTrackIndex == -1) {
realTrackIndex = [self findTheMinTotalWidthTrack];
}
LXDanMuTrackView *trackView = _trackArray[realTrackIndex];
[trackView appendWithCustomView:customView];
}
/**
找到最短右侧出屏幕和等待数组总长度最短的轨道序号
@return 轨道的序号
*/
- (int)findTheMinTotalWidthTrack
{
LXDanMuTrackView *firstTrackView = _trackArray[0];
int minWidthIndex = 0;
float minWidth = [firstTrackView rightOutScreenWidth];
for (int i = 0; i<_trackArray.count; i++)
{
LXDanMuTrackView *trackView = _trackArray[i];
float trackRightOutScreenWidth = [trackView rightOutScreenWidth];
if (trackRightOutScreenWidth < minWidth)
{
minWidth = trackRightOutScreenWidth;
minWidthIndex = i;
}
}
return minWidthIndex;
}
@end
@@ -0,0 +1,19 @@
//
// LXDanMuManagerConfig.h
// LXAnimationTest
//
// Created by lianxiang on 2018/8/14.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#ifndef LXDanMuManagerConfig_h
#define LXDanMuManagerConfig_h
//轨道中弹幕的最小间距
#define TRACK_HORIZONTAL_PADDING 10
//轨道与轨道的纵向间距
#define TRACK_VERTICAL_PADDING 10
//轨道的高度(轨道上的View不能超过此高度)
#define TRACK_HEIGHT 60
#endif /* LXDanMuManagerConfig_h */
@@ -0,0 +1,47 @@
//
// LXDanMuTrackView.h
// LXAnimationTest
//
// Created by lianxiang on 2018/8/14.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LXDanMuManagerConfig.h"
@interface LXDanMuTrackView : UIView
/**
等待出现到屏幕中的弹幕view数组
*/
@property (nonatomic,strong) NSMutableArray *waitShowArray;
/**
正在展示的弹幕view数组
*/
@property (nonatomic,strong) NSMutableArray *showingArray;
/**
向轨道中插入一条自定义的view
@param view 自定义的view,高度不可超过轨道的高,否则出问题
*/
- (void)appendWithCustomView:(UIView *)view;
/**
当前轨道右侧出屏幕的距离+等待出现到屏幕中的弹幕view的总长度和
@return 等待弹幕+右侧出屏幕弹幕的和
*/
- (float)rightOutScreenWidth;
/**
清理轨道,移除所有弹幕
*/
- (void)cleanTrack;
/**
根据速度移动轨道中所有弹幕的位置
@param speed 每0.01秒弹幕向左移动的距离
*/
- (void)moveViewWithSpeed:(float)speed;
@end
@@ -0,0 +1,123 @@
//
// LXDanMuTrackView.m
// LXAnimationTest
//
// Created by lianxiang on 2018/8/14.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import "LXDanMuTrackView.h"
@implementation LXDanMuTrackView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self dataConfig];
}
return self;
}
-(void)dataConfig{
_showingArray = [[NSMutableArray alloc] init];
_waitShowArray = [[NSMutableArray alloc] init];
}
- (void)appendWithCustomView:(UIView *)view{
view.frame = CGRectMake(self.frame.size.width,view.frame.origin.y, view.frame.size.width,view.frame.size.height);
view.center = CGPointMake(view.center.x, TRACK_HEIGHT/2);
if (_showingArray.count) {
UIView *lastView = [_showingArray lastObject];
//最右侧的那条弹幕还没有完全出现在屏幕中
if (CGRectGetMaxX(lastView.frame) + TRACK_HORIZONTAL_PADDING > self.frame.size.width || _waitShowArray.count) {
//把这条弹幕加到等待数组中
[_waitShowArray addObject:view];
}else{
//加入轨道
[self addSubview:view];
//加入展示数组
[_showingArray addObject:view];
}
}else{
[self addSubview:view];
[_showingArray addObject:view];
}
}
- (void)moveViewWithSpeed:(float)speed{
//正在展示的弹幕自减一次
for (UIView *view in _showingArray) {
view.frame = CGRectMake(view.frame.origin.x - speed, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
}
//正在展示的弹幕
if (_showingArray.count){
UIView *firstView = [_showingArray firstObject];
UIView *lastView = [_showingArray lastObject];
if (CGRectGetMaxX(firstView.frame) < 0) {
//已移除屏幕 remove
[firstView removeFromSuperview];
[_showingArray removeObjectAtIndex:0];
}
if (_waitShowArray.count) {
//最右侧的view 已经完全出现
if (CGRectGetMaxX(lastView.frame) + TRACK_HORIZONTAL_PADDING <= self.frame.size.width) {
//转移数组
[_showingArray addObject:[_waitShowArray firstObject]];
[_waitShowArray removeObjectAtIndex:0];
[self addSubview:[_showingArray lastObject]];
}
}
}
}
- (float)rightOutScreenWidth
{
float outScreenWidth = 0.0;
//如果等待数组中有值
if (_waitShowArray.count)
{
//先遍历所有等待数组中的view,累加长度和间距
for (UIView *view in _waitShowArray)
{
outScreenWidth += view.frame.size.width + TRACK_HORIZONTAL_PADDING;
}
}
//如果展示数组中有值
if (_showingArray.count)
{
//拿出最右侧的view
UIView *lastView = [_showingArray lastObject];
//算出出屏宽
float rightOutWidth = CGRectGetMaxX(lastView.frame) - self.frame.size.width;
if (rightOutWidth < 0)
{
rightOutWidth = 0;
}
outScreenWidth += rightOutWidth;
}
return outScreenWidth;
}
//清理轨道
- (void)cleanTrack
{
for (UIView *view in _showingArray)
{
[view removeFromSuperview];
}
[_showingArray removeAllObjects];
[_waitShowArray removeAllObjects];
}
@end
@@ -0,0 +1,25 @@
//
// GiGaSocketRocketUtility.h
// LXAnimationTest
//
// Created by lianxiang on 2018/8/17.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "SocketRocket.h"
extern NSString * const kNeedPayOrderNote;
extern NSString * const kWebSocketDidOpenNote;
extern NSString * const kWebSocketDidCloseNote;
extern NSString * const kWebSocketdidReceiveMessageNote;
@interface GiGaSocketRocketUtility : NSObject
// 获取连接状态
@property (nonatomic,assign,readonly) SRReadyState socketReadyState;
+ (GiGaSocketRocketUtility *)shareInstance;
-(void)srWebSocketOpenWithURLString:(NSString *)urlString;//开启连接
-(void)srWebSocketClose;//关闭连接
- (void)sendData:(id)data;//发送数据
@end
@@ -0,0 +1,248 @@
//
// GiGaSocketRocketUtility.m
// LXAnimationTest
//
// Created by lianxiang on 2018/8/17.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import "GiGaSocketRocketUtility.h"
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
NSString * const kNeedPayOrderNote = @"kNeedPayOrderNote";//发送的通知名
NSString * const kWebSocketDidOpenNote = @"kWebSocketdidReceiveMessageNote";
NSString * const kWebSocketDidCloseNote = @"kWebSocketDidCloseNote";
NSString * const kWebSocketdidReceiveMessageNote = @"kWebSocketdidReceiveMessageNote";
@interface GiGaSocketRocketUtility()<SRWebSocketDelegate>
{
int _index;
NSTimer *heartBeat;
NSTimeInterval reConnectTime;
}
@property (nonatomic,strong) SRWebSocket *socket;
@property (nonatomic,copy) NSString *urlString;
@end
@implementation GiGaSocketRocketUtility
+(GiGaSocketRocketUtility *)shareInstance{
static GiGaSocketRocketUtility *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
//开启链接
-(void)srWebSocketOpenWithURLString:(NSString *)url{
if (!self.socket) {
return;
}
if (!url) {
return;
}
self.urlString = url;
self.socket = [[SRWebSocket alloc] initWithURLRequest: [NSURLRequest requestWithURL:[NSURL URLWithString:url]]] ;
NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString);
self.socket.delegate = self;
[self.socket open];
}
//关闭连接
-(void)srWebSocketClose{
if (self.socket) {
[self.socket close];
self.socket = nil;
[self removeHeartBeat];
}
}
//移除心跳
-(void)removeHeartBeat{
dispatch_main_async_safe(^{
if (self->heartBeat) {
if ([self->heartBeat respondsToSelector:@selector(isValid)]) {
if ([self->heartBeat isValid]) {
[self->heartBeat invalidate];
self->heartBeat = nil;
}
}
}
}
)
}
//初始化心跳
- (void)initHeartBeat
{
dispatch_main_async_safe(^{
[self removeHeartBeat];
//心跳设置为3分钟,NAT超时一般为5分钟
self->heartBeat = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(sentheart) userInfo:nil repeats:YES];
//和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
[[NSRunLoop currentRunLoop] addTimer:self->heartBeat forMode:NSRunLoopCommonModes];
})
}
-(void)sentheart{
//发送心跳 和后台可以约定发送什么内容 一般可以调用ping 这里发送data
[self sendData:@"heart"];
}
#define WeakSelf(ws) __weak __typeof(&*self)weakSelf = self
- (void)sendData:(id)data {
NSLog(@"socketSendData --------------- %@",data);
WeakSelf(ws);
dispatch_queue_t queue = dispatch_queue_create("giga", NULL);
dispatch_async(queue, ^{
if (weakSelf.socket != nil) {
// 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
if (weakSelf.socket.readyState == SR_OPEN) {
[weakSelf.socket send:data]; // 发送数据
} else if (weakSelf.socket.readyState == SR_CONNECTING) {
NSLog(@"正在连接中,重连后其他方法会去自动同步数据");
// 每隔2秒检测一次 socket.readyState 状态,检测 10 次左右
// 只要有一次状态是 SR_OPEN 的就调用 [ws.socket send:data] 发送数据
// 如果 10 次都还是没连上的,那这个发送请求就丢失了,这种情况是服务器的问题了,小概率的
//重连
[self reConnect];
} else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {
// websocket 断开了,调用 reConnect 方法重连
NSLog(@"重连");
[self reConnect];
}
} else {
NSLog(@"没网络,发送失败,一旦断网 socket 会被我设置 nil 的");
}
});
}
//pingPong
- (void)ping{
if (self.socket.readyState == SR_OPEN) {
[self.socket sendPing:nil];
}
}
#pragma mark - socket delegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket{
//每次正常连接的时候清零重连时间
reConnectTime = 0;
//开启心跳
[self initHeartBeat];
if (webSocket == self.socket) {
NSLog(@"************************** socket 连接成功************************** ");
[[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidOpenNote object:nil];
}
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
if (webSocket == self.socket) {
NSLog(@"************************** socket 连接失败************************** ");
NSLog(@"err:%@",error);
_socket = nil;
//连接失败就重连
[self reConnect];
}
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
if (webSocket == self.socket) {
NSLog(@"************************** socket连接断开************************** ");
NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
[self srWebSocketClose];
[[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidCloseNote object:nil];
}
}
/*该函数是接收服务器发送的pong消息,其中最后一个是接受pong消息的,
在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包,
用于每隔一段时间通知一次服务端,客户端还是在线,这个心跳包其实就是一个ping消息,
我的理解就是建立一个定时器,每隔十秒或者十五秒向服务端发送一个ping消息,这个消息可是是空的
*/
-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
NSLog(@"pongPayload===%@",reply);
}
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
if (webSocket == self.socket) {
NSLog(@"************************** socket收到数据了************************** ");
NSLog(@"我这后台约定的 message 是 json 格式数据收到数据,就按格式解析吧,然后把数据发给调用层");
NSLog(@"message:%@",message);
[[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];
}
}
#pragma mark methods
//重连机制
-(void)reConnect{
[self srWebSocketClose];
//超过一分钟就不再重连 所以只会重连5次 2^5 = 64
if (reConnectTime > 64) {
return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.socket = nil;
[self srWebSocketOpenWithURLString:self.urlString];
NSLog(@"重连");
});
//重连时间2的指数级增长
if (reConnectTime ==0 ) {
reConnectTime = 2;
}else{
reConnectTime *=2;
}
}
- (SRReadyState)socketReadyState{
return self.socket.readyState;
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end