0%

要告别 SDK 开发工作了,写篇文章纪念下这段美好的岁月吧!

追求卓越的20个月

从 16 年 5 月以来,我就开始从事 SDK 的开发工作,我觉得做 SDK 跟 App 是有很大差别的,因为 SDK 需要考虑很多合理性,既要满足 App 调用方便又要保证其通用性,优雅性,内外兼修,有的放矢,做 App 也许就不会考虑这么多的方面了,我和同事交流最多的就是问 : “xxx,这样做你觉得合理吗?App 调用方便吗?是否通用,日后是否可以扩展?”

我抽时间写了一些做 SDK 过程中解决的问题,代码设计思路相关的文章,单独拎了一个 千帆SDK 的类别。

Productive Forces

17 年这一年我每天都是 7 点多去公司,所以有了更多的业余时间,我写了一个简单的自动化远程打包服务器,实现了完全脚本话打包,打完后自动部署到 Apache 服务器;需要发包时,只需提交下代码,改下 build 号,等上 2 分钟就打好了,然后复制下载地址使用邮件发送即可;这比起人工打包,然后合并 framework,节省了不少宝贵的时间。

Products

很期待你能去体验下我们公司的产品和我们 SDK 提供的服务:

SDKs 集成渠道
千帆直播 SDK 搜狐视频、搜狐新闻、搜狐汽车,狗仔直播
搜狐课堂 SDK 搜狐视频
Game SDK (答题PK、水果转盘、题王争霸、成语接龙) 千帆直播
小视频 SDK 千帆直播
播放器 SDK 360影视大全,搜狗搜索,百度视频

Bye,My 2017 !

今天是 2017 年最后一天,上午约了朋友去外滩吃火锅,饭后去了新开业的滨海万达广场,建设的还挺不错的,四楼有很多美食,以后倒是可以经常去约个饭,看个电影啥的…

回首 2017,着实经历了不少事情,可真到写的时候不知道写什么了,还是按照时间顺序说几个大事吧:

2月

大组来了三个应届生,其中两个是我组的,有一个后来也转到我组了,这样以来我组规模更大了,有人了就得干些事出来才行!

4月

去了趟广州,主要是为接下来的千帆小游戏布局,紧接着我们做了好几款小游戏,其中包括:快捷答题,水果转盘,成语接龙,答题 PK 等,SDK 的开发工作基本也到了鼎盛时期,因为此时 SDK 的数量最多,为此我还专门搞了打包服务器,远程打包发布,集成方从服务器下载更新。

在广州出差时,领导突然找到我说要给我升下 level,当时是 2.2,升过之后是 3.1(在搜狐是个高级职称),即高级开发工程师,我是一个技术狂,能够拿到高级也是我一直苦苦追求的!

6月

领导决定将组内的汇报工作全部转移给我(之前都是统一汇报给领导,我是个有实无名的 leader),就这样我开始学习管理经验,但也没有因此抛弃写代码,因为我很喜欢 coding !

7月

拿到了天津的集体户口,来这的目的总算是达到了,就差买房落户了。

10 月

开始使用游戏引擎编写游戏,前面说到的几个小游戏都是使用 iOS Native UI 写的,没啥技术含量😓,这次我们选择了 Apple 的 SpriteKit 2d 引擎开始写斗地主,从开始码人到出来用了两个多月的时间,与此同时进行的还有使用 cocos2d-x 的安卓端,由于 iOS 使用的是原生平台游戏引擎,自然少了很多坑,没有内存,性能等一系列问题;安卓端则遇到了很多问题,所以他们进度稍慢些,所以 iOS 版最先开始公测的。

12 月

不寻常的一个月份了,某个早晨,我要找领导汇报点工作,结果他说他先给我讲点事情,被告知公司成立了一个创新平台事业部,部门组织架构要调整下,变化还挺大的,我们组要换领导了(😢),工作上变大也挺大的,要告别 SDKs 开发了,从此以后要专注于游戏开发,大早上听到这个消息我蛮震惊的,我工作上不喜欢换领导,前两次跳槽也都跟换领导有很大的关系。紧接着就是一面交接 SDKs,一面学习游戏开发,随着千帆 SDK3.1 封板,我彻底告别了做了 20 个月的最求卓越的 SDK 开发工作,十分不舍得,但是又没有办法,不舍是因为如果没有千帆 SDK 就没有我来天津这件事,也没有现在我带起来的这帮人,不得不承认在做 SDK 开发的这段时间摸索到了很多 SDK 开发的经验,期间做了很多的尝试与创新!封装了一批自己的库ಥ_ಥ,真真切切的写了好多代码,思考了很多,进步颇多,感谢团队,感恩我的领导!

除了这些之外,我们组织了好多次代码评审,十多次技术分享,十几篇 issue,人人都有参与其中,大家都从中受益了。

2018 计划

拥抱变化,努力做好主管工作,带领团队走向光明,为游戏事业奋斗到底!

说点高兴的话题吧,那就是 2018 年,我要当爸爸了,敲锣打鼓迎接我的 “狗宝” !

在做歌词显示的时候需要实现文字渐变消失的效果,向同事请教后知道了一个实现方法:使用 CAGradientLayer 来做,但是对CAGradientLayer 的属性设置不是很明确,所以进行了总结,现将代码贴出来并做解释说明,方便大家使用:

实现效果

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)addLayerToView:(UIView *)view width:(CGFloat)width {
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(0, 0, width, view.bounds.size.height);;
gradientLayer.colors = @[(__bridge id)[UIColor clearColor].CGColor,(__bridge id)[UIColor blackColor].CGColor,(__bridge id)[UIColor blackColor].CGColor,(__bridge id)[UIColor clearColor].CGColor];
gradientLayer.locations = @[@(0),@(0.1),@(0.9),@(1)];
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(0, 1);
CGRect frame = view.bounds;

UIView *gradientView = [[UIView alloc] initWithFrame:frame];
[gradientView.layer addSublayer:gradientLayer];
view.maskView = gradientView;
}

CAGradientLayer属性设置

上面代码主要涉及CAGradientLayer的四个属性colors locations startPoint endPoint,colors是颜色分配,locations是颜色分割线,startPoint为起始点,endPoint为终止点。

从上图可以看出,locations里面的值代表颜色的分界线,如果想要实现view的上下端都有渐变消失的效果,需要四个颜色和四个颜色分割线,也就是从0到0.1是clearColor -> blackColor,从0.1-0.9不变色所以是blackColor-> blackColor,从0.9-1是blackColor -> clearColor。。。这样就实现了只有显示文字的view的最上端和最下端出现渐变效果,如上图所示。

由于开发需要实现一个随音乐播放滚动歌词的音乐提词器,通过网上查找资料,实现了效果,将代码贴出来,方便大家使用,主要步骤如下:

歌词解析

TEMusicLrcParser.h

1
2
3
4
5
6
7
8
9
10
11
12
#import <Foundation/Foundation.h>

@interface LrcParser : NSObject

//时间
@property (nonatomic,strong) NSMutableArray *timerArray;
//歌词
@property (nonatomic,strong) NSMutableArray *wordArray;

//解析歌词
-(void) parseLrc:(NSString*)lrc;
@end

TEMusicLrcParser.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#import "TEMusicLrcParser.h"

@interface LrcParser ()

-(void) parseLrc:(NSString *)word;

@end

@implementation LrcParser

-(instancetype) init{
self=[super init];
if(self!=nil){
self.timerArray=[[NSMutableArray alloc] init];
self.wordArray=[[NSMutableArray alloc] init];
}
return self;
}


-(void)parseLrc:(NSString *)lrc{
NSLog(@"%@",lrc);

if(![lrc isEqual:nil]){
NSArray *sepArray=[lrc componentsSeparatedByString:@"["];
NSArray *lineArray=[[NSArray alloc] init];
[self.wordArray removeAllObjects];
[self.timerArray removeAllObjects];
for(int i=0;i<sepArray.count;i++){
if([sepArray[i] length]>0){
lineArray=[sepArray[i] componentsSeparatedByString:@"]"];
if(![lineArray[0] isEqualToString:@"\n"]){
[self.timerArray addObject:lineArray[0]];

[self.wordArray addObject:lineArray.count>1?lineArray[1]:@""];
}
}
}
}
}
@end

歌词解析部分主要是将歌词格式的字符串切分开,wordArray里面是歌词,timerArray里面是每句时间点

实现歌词的滚动

使用tableview来显示歌词

TEMusicLrcView.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "TEMusicLrcParser.h"


@protocol TEMusicLrcViewDelegate <NSObject>
@optional
-(void) updateTime;//根据音乐播放器的时间更新时间,代理中实现
@end

@interface TEMusicLrcView : UITableView <UITableViewDataSource,UITableViewDelegate>
@property (strong,nonatomic) LrcParser* lrcContent;
@property (assign) NSInteger currentRow;

@property (nonatomic, weak) id<TEMusicLrcViewDelegate> musicLrcViewDelegate;
@property (nonatomic,strong) AVAudioPlayer *player;
@property (nonatomic,strong) NSString *musicTotalLrc;//整首歌的歌词解析前的字符串
-(instancetype)initWithFrame:(CGRect)frame musicTotalLrc:(NSString *)musicTotalLrc;
@end

TEMusicLrcView.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

#import "TEMusicLrcView.h"

#import "TECaptureViewController.h"

@interface TEMusicLrcView ()
@property (nonatomic,strong) NSTimer *timer;

@end


@implementation TEMusicLrcView

-(instancetype)initWithFrame:(CGRect)frame musicTotalLrc:(NSString *)musicTotalLrc{
self = [super initWithFrame:frame];
if(self)
{
self.delegate=self;
self.dataSource=self;
self.lrcContent=[[LrcParser alloc] init];
self.separatorStyle = UITableViewCellSeparatorStyleNone;
self.allowsSelection = NO;

[self.lrcContent parseLrc:musicTotalLrc];
[self reloadData];
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(update) userInfo:nil repeats:YES];

UIImageView *bgView=[[UIImageView alloc] init];
bgView.backgroundColor = [UIColor clearColor];
//bgView.alpha=0.8;
self.backgroundView=bgView;
}
return self;
}


-(void)update{
[self.musicLrcViewDelegate updateTime];
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.lrcContent.wordArray.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row==_currentRow){
return 60.0/kVisualFactor;
}
else{
return 54.0/kVisualFactor;
}
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"TEMusicLrcCell"];
UITableViewCell *cell=[self dequeueReusableCellWithIdentifier:@"TEMusicLrcCell" forIndexPath:indexPath];

cell.textLabel.text=self.lrcContent.wordArray[indexPath.row];

if(indexPath.row==_currentRow){
cell.textLabel.textColor = [UIColor colorFromHex:@"ffda44"];
cell.textLabel.font = [UIFont systemFontOfSize:34.0/kVisualFactor];
cell.textLabel.layer.shadowColor = [UIColor clearColor].CGColor;
cell.textLabel.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
}
else{
cell.textLabel.textColor = [UIColor colorWithWhite:1 alpha:0.7];
cell.textLabel.font = [UIFont systemFontOfSize:28.0/kVisualFactor];
cell.textLabel.layer.shadowOpacity = 1.0;
cell.textLabel.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.4].CGColor;
cell.textLabel.layer.shadowOffset = CGSizeMake(0.0f, 1.0f);
cell.textLabel.layer.shadowRadius = 2;
}
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.backgroundColor=[UIColor clearColor];

return cell;
}


@end

其中更新提词器的时间的实现方法是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#pragma mark - 更新提词器时间
-(void) updateTime{
CGFloat currentTime=self.musicPlayer.currentlocation;
// NSLog(@"%d:%d",(int)currentTime / 60, (int)currentTime % 60);
for (int i=0; i<self.musicLrcView.lrcContent.timerArray.count; i++) {
NSArray *timeArray=[self.musicLrcView.lrcContent.timerArray[i] componentsSeparatedByString:@":"];
float lrcTime=[timeArray[0] intValue]*60+[timeArray[1] floatValue];
if(currentTime>lrcTime){//过了这句歌词的起始时间后,刷新当前歌词
self.musicLrcView.currentRow=i;
}else
break;
}

[self.musicLrcView reloadData];
[self.musicLrcView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.musicLrcView.currentRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}

currentTime为当前播放器的时间,lrcTime为当前歌词的开始时间,过了这句歌词的起始时间后,刷新当前歌词