使用网易云音乐也是一个巧合,我之前一直使用QQ音乐听歌,前几天下 app 手机内存告急,于是就把QQ音乐给卸载掉了,正好晚上朋友圈里有一个朋友用网易云音乐分享了一首歌曲,于是我也就尝试下载了网易云音乐,这一下载就让我从QQ音乐粉转黑了。

从设计的角度来看,网易云音乐的界面简洁,大方,不像kugou音乐一打开就是各种广告,让人心烦。也不像QQ音乐那样动不动就各种音质,各种冲钻(不为用户需求考虑,只想赚钱,差评),最关键的是它推荐的歌真是好听,实在是太懂我了,真的是很用心的在做音乐。

废话不多说了,今天给大家带来一篇山寨网易云音乐歌词海报生成的文章,自从发现了这个功能,我已经在朋友圈刷屏了。既然这么喜欢,那为何不自己来实现一下呢!

首先,有些童鞋可能还不清楚什么是歌词海报,我在这里就先简单的作一个说明:我们在听歌的时候难免会有那么几句歌词在脑海中余音缭绕,网易云音乐就有这么一个功能,你可以查看你喜欢的歌词然后选中它们,然后App会将这些歌词附加到那些文艺的背景中去生成一张海报,这样你就可以将它分享到你的朋友圈里去,做一个装逼的文艺青年。

设计思路:

  1. 解析歌词文件,在界面上用UITableView加载
  2. 长按界面,将UITableView切换至可编辑状态
  3. 将选中的歌词保存
  4. 根据歌词的数量在UIImageView上动态创建UILabel
  5. 将UIImageView保存为图片存至相册

代码实现:

目前代码解析的歌词文件都是lrc的格式,例如网易,QQ,他们都有自己的海量的歌词数据,在网上搜索歌词文件也能搜索到很多,例如次样式的:

[00:01.16] 不为谁而作的歌 - 林俊杰
[00:05.96] 词:林秋离
[00:08.16] 曲:林俊杰
[00:27.51] 原谅我这一首不为谁而作的歌
[00:34.12] 感觉上仿佛窗外的夜色
[00:41.24] 曾经有那一刻
[00:44.18] 回头竟然认不得需要从记忆再摸索的人
[00:54.85] 和他们关心的 的地方
[01:01.50] 和那些走过的请等一等
[01:10.55] 梦为努力浇了水

有了歌词文件还不行,我们得把歌词和时间都解析出来,这就要用到我们的歌词解析功能了,代码如下:

#import <Foundation/Foundation.h>@interface LrcParseUtil : NSObject
//时间
@property (nonatomic,strong) NSMutableArray *timerArray;
//歌词
@property (nonatomic,strong) NSMutableArray *wordArray;-(NSString *)getLrcFile:(NSString *)lrc;//解析歌词
-(void) parseLrc:(NSString*)lrc;@end
#import "LrcParseUtil.h"@implementation LrcParseUtil
@synthesize timerArray = _timerArray;
@synthesize wordArray = _wordArray;-(instancetype) init{self=[super init];if(self!=nil){self.timerArray=[[NSMutableArray alloc] init];self.wordArray=[[NSMutableArray alloc] init];}return  self;
}-(NSString *)getLrcFile:(NSString *)lrc{NSString* filePath=[[NSBundle mainBundle] pathForResource:lrc ofType:@"lrc"];return  [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
}-(void)parseLrc:(NSString *)lrc{if(![lrc isEqual:nil]){NSArray *sepArray=[lrc componentsSeparatedByString:@"["];NSArray *lineArray=[[NSArray alloc] init];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

上面我们只是将歌词文件转化为数据存储到了我们的内存中,接下来要把这些数据显示给用户,这里我们就要用到UITableView这个强大的控件了,至于这个空间的使用我这里就不在阐述了,代码如下:

@interface ViewController ()@end@implementation ViewController
@synthesize lrcTableView = _lrcTableView;
@synthesize parseUtil = _parseUtil;
@synthesize selectLrcView = _selectLrcView;- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.self.title = @"Silicon Demo";[self.view setBackgroundColor:[UIColor blackColor]];self.lrcTableView = [[UITableView alloc] initWithFrame:self.view.bounds];self.lrcTableView.delegate = self;self.lrcTableView.dataSource = self;[self.lrcTableView setBackgroundColor:[UIColor clearColor]];self.lrcTableView.separatorStyle = UITableViewCellSeparatorStyleNone;[self.view addSubview:self.lrcTableView];//解析歌词self.parseUtil = [[LrcParseUtil alloc] init];[self.parseUtil parseLrc:[self.parseUtil getLrcFile:@"demoSong"]];
}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}#pragma -mark tableview
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{return 40.0f;
}- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{if(!self.selectLrcView){self.selectLrcView = [SelectLrcViewController new];}[self.selectLrcView setLrcData:self.parseUtil.wordArray];CATransition *animation = [CATransition animation];[animation setDuration:0.3];[animation setType: kCATransitionFade];[animation setSubtype: kCATransitionFromTop];[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];[self.navigationController pushViewController:self.selectLrcView animated:NO];[self.navigationController.view.layer addAnimation:animation forKey:nil];
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{return [self.parseUtil.wordArray count];
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{UITableViewCell *cell = [self.lrcTableView dequeueReusableCellWithIdentifier:@"cell"];if(!cell){cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];}cell.textLabel.text = self.parseUtil.wordArray[indexPath.row];cell.textLabel.textColor = [UIColor whiteColor];cell.textLabel.textAlignment = NSTextAlignmentCenter;cell.textLabel.font = [UIFont systemFontOfSize:15];cell.backgroundColor=[UIColor clearColor];return cell;
}@end

效果如下:


UITableView控件原生自带了选择功能,所以我这边图省力就先用原生自带的实现歌词选择功能(日后会更新成自定义的), 代码如下:

#import "SelectLrcViewController.h"@interface SelectLrcViewController ()@end@implementation SelectLrcViewController
@synthesize lrcWordsArray = _lrcWordsArray;
@synthesize wordsTableView = _wordsTableView;
@synthesize selectedLrcs = _selectedLrcs;- (instancetype)init{self = [super init];self.lrcWordsArray = [[NSMutableArray alloc] init];self.selectedLrcs = [[NSMutableArray alloc] init];return self;
}- (void)viewDidLoad {[super viewDidLoad];NSLog(@"%@", self.navigationController.navigationBar);// Do any additional setup after loading the view.UIBarButtonItem *tempBarItem = [[UIBarButtonItem alloc] initWithTitle:@"生成图片" style:UIBarButtonItemStylePlain target:self action:@selector(generatePic)];self.navigationItem.rightBarButtonItem = tempBarItem;self.wordsTableView = [[UITableView alloc] initWithFrame:self.view.bounds];self.wordsTableView.delegate = self;self.wordsTableView.dataSource = self;[self.wordsTableView setBackgroundColor:[UIColor clearColor]];self.wordsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;self.wordsTableView.editing = YES;self.wordsTableView.allowsMultipleSelectionDuringEditing = YES;[self.view addSubview:self.wordsTableView];
}- (void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];[self.wordsTableView reloadData];
}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.}- (void)setLrcData:(NSMutableArray *)lrcdata{self.lrcWordsArray = lrcdata;
}- (void)generatePic{NSArray *selectIndex = [self.wordsTableView indexPathsForSelectedRows];if([selectIndex count] == 0 || !selectIndex){NSLog(@"请选择歌词");return;}[self.selectedLrcs removeAllObjects];for (int i = 0; i < [selectIndex count]; i++) {NSIndexPath *index = selectIndex[i];[self.selectedLrcs addObject:[self.lrcWordsArray objectAtIndex:index.row]];}if([self.selectedLrcs count] == 0){return;}if(!self.lrcShareView){self.lrcShareView = [LrcShareViewController new];}[self.lrcShareView setLrcContent:self.selectedLrcs];CATransition *animation = [CATransition animation];[animation setDuration:0.3];[animation setType: kCATransitionFade];[animation setSubtype: kCATransitionFromTop];[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];[self.navigationController pushViewController:self.lrcShareView animated:NO];[self.navigationController.view.layer addAnimation:animation forKey:nil];
}#pragma -mark tableview
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{return 40.0f;
}- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{NSLog(@"");
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{return [self.lrcWordsArray count];
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{UITableViewCell *cell = [self.wordsTableView dequeueReusableCellWithIdentifier:@"cell"];if(!cell){cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];}cell.textLabel.text = self.lrcWordsArray[indexPath.row];cell.textLabel.textColor = [UIColor whiteColor];cell.textLabel.textAlignment = NSTextAlignmentCenter;cell.textLabel.font = [UIFont systemFontOfSize:15];cell.backgroundColor=[UIColor clearColor];return cell;
}

效果如下:


最后一步就是生成歌词海报了,考虑到图片资源对App安装包大小造成的影响,这里采用了对背景图片进行拉伸的办法,在iOS 5.0之前,我们用此API进行对图片的拉伸:

- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight;

往往会将拉伸的大小设置为1像素,然后保证其他的地方不变,这样纵使我们的控件大小再怎么改变,图片也不会出现拉伸的情况。但这个API在iOS 5之后就被废弃了,在这里我们该用它iOS 6以后新出的AP对图进行拉伸:

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode

有人可能会问,为什么不用图形上下文的方式在背景图片上绘制文字,我之前已尝试过但是生成海报后的像素实在是令人捉急,于是就把这个思路给pass了;后来经过仔细分析,通过在UIImageView中添加subView也就是UILabel,然后通过以下代码生成的海报达到的效果令人满意,代码如下:

 UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, YES, 0.0);[self.imageView.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *bitmap = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();

这里附上,生成海报的全部代码:

#import "LrcShareViewController.h"
#import "ConstantUtil.h"
@interface LrcShareViewController ()@end@implementation LrcShareViewController
@synthesize shareLrcs = _shareLrcs;
@synthesize imageView = _imageView;
@synthesize scrollView = _scrollView;
@synthesize paperLayer = _paperLayer;
@synthesize shareBtn = _shareBtn;
@synthesize saveBtn = _saveBtn;
@synthesize pane = _pane;- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.//set scrollView and share & save Btn_scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];_scrollView.backgroundColor = [UIColor clearColor];_scrollView.delegate = self;_scrollView.contentSize = self.view.bounds.size;_scrollView.scrollEnabled = YES;[self.view addSubview:_scrollView];[self addShareAndSaveBtn];
}- (void)viewWillAppear:(BOOL)animated{// init the imageViewself.imageView = [UIImageView new];// resize the background image , adapte to the lrcsself.paperLayer = [self drawLrcsWithImageContext:[UIImage imageNamed:@"simple.png"]];[self.imageView setImage:self.paperLayer];//add labels on the imageView[self addLyricToBackground:self.shareLrcs];[self.scrollView addSubview:self.imageView];
}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}/**@brief 添加分享与保存按钮*/
- (void)addShareAndSaveBtn{self.pane = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height - 35.0f, self.view.bounds.size.width, 35.f)];[self.pane setBackgroundColor:[UIColor clearColor]];self.shareBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width / 2, 35.0f)];[self.shareBtn setBackgroundColor:[UIColor colorWithRed:23.0f green:24.0f blue:24.0f alpha:0]];[self.shareBtn setTitle:@"分享" forState:UIControlStateNormal];[self.shareBtn setTintColor:[UIColor whiteColor]];[self.shareBtn addTarget:self action:@selector(socialCCshare) forControlEvents:UIControlEventTouchDown];self.saveBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2 + 1, 0, self.view.bounds.size.width / 2, 35.0f)];[self.saveBtn setBackgroundColor:[UIColor colorWithRed:23.0f green:24.0f blue:24.0f alpha:0]];[self.saveBtn setTitle:@"保存" forState:UIControlStateNormal];[self.saveBtn setTintColor:[UIColor whiteColor]];[self.saveBtn addTarget:self action:@selector(saveToPhoto) forControlEvents:UIControlEventTouchDown];UIImageView *line = [[UIImageView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2, 0, 1, 35)];[line setBackgroundColor:[UIColor whiteColor]];[self.pane addSubview:self.shareBtn];[self.pane addSubview:self.saveBtn];[self.pane addSubview:line];[self.view addSubview:self.pane];
}- (void)socialCCshare{NSLog(@"分享");
}/**@brief 保存至手机相册*/
- (void)saveToPhoto{UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, YES, 0.0);[self.imageView.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *bitmap = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();UIImageWriteToSavedPhotosAlbum(bitmap, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
}- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo
{NSString *msg = nil ;if(error != NULL){msg = @"保存图片失败" ;}else{msg = @"保存图片成功" ;}NSLog(@"%@", msg);
}- (void)setLrcContent:(NSMutableArray *)selectLrcs{self.shareLrcs = selectLrcs;
}/**@brief 动态添加歌词到UIImageView*@param lrcsArray 歌词*/
- (void)addLyricToBackground:(NSMutableArray *)lrcsArray{CGFloat point_x = 40.0f;CGFloat point_y = 50.0f;CGFloat t_per_size = 15.0f;CGFloat row_height = 20.0f;CGFloat margin = 10.0f;for(int i = 0; i < [lrcsArray count]; i++){//get lrc from arrayNSString *lrc = [lrcsArray objectAtIndex:i];int lrcLen = lrc.length;//create a label to show the lrcUILabel *lrcLabel = [[UILabel alloc] initWithFrame:CGRectMake(point_x, point_y + i * (row_height + margin), lrcLen * t_per_size, row_height)];[lrcLabel setText:lrc];lrcLabel.font = [UIFont fontWithName:@"Arial" size:15];lrcLabel.textColor = [UIColor lightGrayColor];[self.imageView addSubview:lrcLabel];}//note the song's nameNSString *songName = @"── [不为谁而做的歌]";CGFloat y_songName = self.imageView.frame.size.height - 90.0f;CGFloat width_songName = self.imageView.frame.size.width - 80.0f;UILabel *songFrom = [[UILabel alloc] initWithFrame:CGRectMake((self.view.bounds.size.width - width_songName)/2, y_songName, width_songName, row_height)];[songFrom setText:songName];songFrom.font = [UIFont fontWithName:@"Arial" size:15];songFrom.textColor = [UIColor lightGrayColor];[songFrom setTextAlignment:NSTextAlignmentRight];[self.imageView addSubview:songFrom];
}/** @brief 拉伸背景图片达到满足背景的要求* @param layerImage 背景图片*/
- (UIImage *)drawLrcsWithImageContext:(UIImage *)layerImage{CGFloat rowHeight = 20.0f;CGFloat margins = 5.0f;/**背景海报的高度*header iphone 固定为80px*footer iphone 固定为120px*/CGFloat imageHeight = (rowHeight + margins) * [self.shareLrcs count];//背景海报的宽度为屏幕固定宽度CGFloat imageWidth = self.view.bounds.size.width;[self.imageView setFrame:CGRectMake(0, 0, imageWidth, 200 + imageHeight)];CGFloat top = layerImage.size.height /2 - 1;CGFloat left = layerImage.size.width /2 - 1;CGFloat bottom = layerImage.size.height /2 - 1;CGFloat right = layerImage.size.width /2 - 1;// 设置端盖的值UIEdgeInsets edgeInsets = UIEdgeInsetsMake(top, left, bottom, right);// 设置拉伸的模式UIImageResizingMode mode = UIImageResizingModeStretch;// 拉伸图片UIImage *newImage = [layerImage resizableImageWithCapInsets:edgeInsets resizingMode:mode];return newImage;
}@end

效果图如下:


总结

此功能在界面效果和用户体验上离网易还差很远,但是基本的核心已经实现,当然实现这种效果可能有100种方法,欢迎大家来指正,我也会继续更新代码像网易靠拢。

GitHub地址:https://github.com/ShenJieSuzhou/https—github.com-ShenJieSuzhou-LrcShareDemo.git


好了。祝大家生活愉快。多多收获友谊和爱情。如果想获取更多的讯息,请扫描下方二维码关注我的微信公众号:

iOS 开发仿网易云音乐歌词海报相关推荐

  1. 尚硅谷微信小程序开发 仿网易云音乐App 小程序 后端接口服务器搭建

    目录 小程序学习 视频相关的教程文档与笔记分享 配套服务器 源码地址: 接口使用说明文档 接口列表 启动服务 测试服务启动OK网页 http://localhost:3000/test.html​编辑 ...

  2. Vue3+Vite+TS独立开发仿网易云音乐Web应用

    项目背景 自己之前的项目要么就是比较小的,要么就是跟着视频敲的,感觉做完之后并没有很好地吸收学到的知识.于是想要自己独立地做一个前端项目,刚好看到网易云音乐的网页端没有它的客户端好看,于是就仿照网易云 ...

  3. 移动应用开发——仿网易云音乐学习心得(uni-app框架)

    目录 一,什么是uni-app框架? 二,为什么要选择使用uni-app框架,uni-app框架的优势又有哪些? uni-app框架的优势 1,开发者/案例数量更多 2,平台能力不受限 3,性能体验优 ...

  4. 移动应用开发——uni-app框架 仿网易云音乐播放器学习心得

    目录 一.uni-app框架介绍 1.什么是 uni-app 2.为什么要选择uni-app 3.uni-app 统一规范 4.uni-app功能框架 二.开发工具与项目创建 1.开发工具 2.项目创 ...

  5. 微信小程序仿网易云音乐(使用云开发,提供源码)

    微信小程序仿网易云音乐(使用云开发,提供源码)!!!!!!!!!!! 源码: 链接:https://pan.baidu.com/s/1z_ZnRVbT4vjEENimi8yBQQ 提取码:u0o3 一 ...

  6. 仿网易云音乐播放列表、皮肤样式、歌词滚动、轮播图等

    前言: 今天是我第二次写博客,打算把以前做过的一些小东西记录下来,今天介绍的是我的毕业设计<小罡音乐>是简仿网易云播放器的一些界面和播放音乐功能. 是基于ASP.NET的小罡音乐的设计与实 ...

  7. Android漂亮的音乐歌词控件,仿网易云音乐滑动效果

    前言: 项目有个音乐播发器功能,实现音乐在线播放,同时需要带有歌词显示功能.网上也找过,在github找到勉强能用的控件,只是效果还是差强人意,不是特别好.于是趁有空的时间,参考了网上的部分demo, ...

  8. node.js+uniapp计算机毕业设计安卓仿网易云音乐客户端APP(程序+APP+LW)

    该项目含有源码.文档.程序.数据库.配套开发软件.软件安装教程.欢迎交流 项目运行 环境配置: Node.js+ Vscode + Mysql5.7 + HBuilderX+Navicat11+Vue ...

  9. 仿网易云音乐(微信小程序版)

    项目部分截图(Gif) 前言 前一阵子学习了微信小程序,为了巩固所学的知识和提高实战经验,决定自己手撸一款小程序.因为听歌一直在用网易云音乐,所以突发奇想就做一款仿网易云音乐的小程序吧!开发中遇到了很 ...

  10. Flutter+FishRedux高仿网易云音乐

    flutter_netease_cloud_music 采用FishRedux框架与开源网易云音乐api开发的高仿网易云音乐APP,技术栈主要是:Flutter+FishRedux,目前主要是偏重AP ...

最新文章

  1. 突破微信小程序五层层级限制的解决方案
  2. 谷歌下一代AI架构、Jeff Dean宣传大半年的Pathways终于有论文了
  3. log4j打印mybatis执行的sql
  4. 2021年春季学期-信号与系统-第四次作业参考答案-第五小题
  5. 【正一专栏】再见小马哥——永记你含着泪的笑
  6. C#进阶系列——使用Advanced Installer制作IIS安装包(二:配置安装包依赖项和自定义dll)...
  7. Chrome 开发者工具 live expression 的用法
  8. 休眠锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作
  9. excel单元格斜线_掌握这20个Excel技巧,小白轻松变大神
  10. 狂神说Java学习笔记 Java基础
  11. Rust学习笔记——模式和匹配
  12. 多个高危 BIOS 缺陷影响英特尔处理器,特斯拉 Model 3 未幸免,可用于供应链攻击...
  13. linux 安装tomcat
  14. 如何总结和整理学术文献?
  15. oracle扩容temp表空间,解决oracle临时表空间的报错
  16. 初看SOA:SOA是什么?
  17. PhantomJS+Selenium爬取淘宝
  18. ubuntu安装迅雷
  19. Spring下载教程(保姆级)
  20. 积极推进市域治理现代化,全力打造国家治理体系新局面

热门文章

  1. oracle regexp
  2. 整理了一些已分类的产品Axure元件库,助力正在找Axure组件库的同学高速成长
  3. 如何在PHP中获得有用的错误消息?
  4. JAVA实现邮件发送
  5. AMPL—快速了解,秒懂它。
  6. 毕业设计 - 基于JAVA人脸识别管理系统(人脸搜索与人脸库管理)
  7. 大数据智能运维平台方案-2
  8. vasp和ms_科学网—小谈CASTEP(Linux), MS-CASTEP, VASP软件区别 - 张召富的博文
  9. 软件测试文档测试用例,软件测试用例文档模板(带实例)
  10. SQL Server 2016安装教程