iOS 开发仿网易云音乐歌词海报
使用网易云音乐也是一个巧合,我之前一直使用QQ音乐听歌,前几天下 app 手机内存告急,于是就把QQ音乐给卸载掉了,正好晚上朋友圈里有一个朋友用网易云音乐分享了一首歌曲,于是我也就尝试下载了网易云音乐,这一下载就让我从QQ音乐粉转黑了。
从设计的角度来看,网易云音乐的界面简洁,大方,不像kugou音乐一打开就是各种广告,让人心烦。也不像QQ音乐那样动不动就各种音质,各种冲钻(不为用户需求考虑,只想赚钱,差评),最关键的是它推荐的歌真是好听,实在是太懂我了,真的是很用心的在做音乐。
废话不多说了,今天给大家带来一篇山寨网易云音乐歌词海报生成的文章,自从发现了这个功能,我已经在朋友圈刷屏了。既然这么喜欢,那为何不自己来实现一下呢!
首先,有些童鞋可能还不清楚什么是歌词海报,我在这里就先简单的作一个说明:我们在听歌的时候难免会有那么几句歌词在脑海中余音缭绕,网易云音乐就有这么一个功能,你可以查看你喜欢的歌词然后选中它们,然后App会将这些歌词附加到那些文艺的背景中去生成一张海报,这样你就可以将它分享到你的朋友圈里去,做一个装逼的文艺青年。
设计思路:
- 解析歌词文件,在界面上用UITableView加载
- 长按界面,将UITableView切换至可编辑状态
- 将选中的歌词保存
- 根据歌词的数量在UIImageView上动态创建UILabel
- 将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 开发仿网易云音乐歌词海报相关推荐
- 尚硅谷微信小程序开发 仿网易云音乐App 小程序 后端接口服务器搭建
目录 小程序学习 视频相关的教程文档与笔记分享 配套服务器 源码地址: 接口使用说明文档 接口列表 启动服务 测试服务启动OK网页 http://localhost:3000/test.html编辑 ...
- Vue3+Vite+TS独立开发仿网易云音乐Web应用
项目背景 自己之前的项目要么就是比较小的,要么就是跟着视频敲的,感觉做完之后并没有很好地吸收学到的知识.于是想要自己独立地做一个前端项目,刚好看到网易云音乐的网页端没有它的客户端好看,于是就仿照网易云 ...
- 移动应用开发——仿网易云音乐学习心得(uni-app框架)
目录 一,什么是uni-app框架? 二,为什么要选择使用uni-app框架,uni-app框架的优势又有哪些? uni-app框架的优势 1,开发者/案例数量更多 2,平台能力不受限 3,性能体验优 ...
- 移动应用开发——uni-app框架 仿网易云音乐播放器学习心得
目录 一.uni-app框架介绍 1.什么是 uni-app 2.为什么要选择uni-app 3.uni-app 统一规范 4.uni-app功能框架 二.开发工具与项目创建 1.开发工具 2.项目创 ...
- 微信小程序仿网易云音乐(使用云开发,提供源码)
微信小程序仿网易云音乐(使用云开发,提供源码)!!!!!!!!!!! 源码: 链接:https://pan.baidu.com/s/1z_ZnRVbT4vjEENimi8yBQQ 提取码:u0o3 一 ...
- 仿网易云音乐播放列表、皮肤样式、歌词滚动、轮播图等
前言: 今天是我第二次写博客,打算把以前做过的一些小东西记录下来,今天介绍的是我的毕业设计<小罡音乐>是简仿网易云播放器的一些界面和播放音乐功能. 是基于ASP.NET的小罡音乐的设计与实 ...
- Android漂亮的音乐歌词控件,仿网易云音乐滑动效果
前言: 项目有个音乐播发器功能,实现音乐在线播放,同时需要带有歌词显示功能.网上也找过,在github找到勉强能用的控件,只是效果还是差强人意,不是特别好.于是趁有空的时间,参考了网上的部分demo, ...
- node.js+uniapp计算机毕业设计安卓仿网易云音乐客户端APP(程序+APP+LW)
该项目含有源码.文档.程序.数据库.配套开发软件.软件安装教程.欢迎交流 项目运行 环境配置: Node.js+ Vscode + Mysql5.7 + HBuilderX+Navicat11+Vue ...
- 仿网易云音乐(微信小程序版)
项目部分截图(Gif) 前言 前一阵子学习了微信小程序,为了巩固所学的知识和提高实战经验,决定自己手撸一款小程序.因为听歌一直在用网易云音乐,所以突发奇想就做一款仿网易云音乐的小程序吧!开发中遇到了很 ...
- Flutter+FishRedux高仿网易云音乐
flutter_netease_cloud_music 采用FishRedux框架与开源网易云音乐api开发的高仿网易云音乐APP,技术栈主要是:Flutter+FishRedux,目前主要是偏重AP ...
最新文章
- 突破微信小程序五层层级限制的解决方案
- 谷歌下一代AI架构、Jeff Dean宣传大半年的Pathways终于有论文了
- log4j打印mybatis执行的sql
- 2021年春季学期-信号与系统-第四次作业参考答案-第五小题
- 【正一专栏】再见小马哥——永记你含着泪的笑
- C#进阶系列——使用Advanced Installer制作IIS安装包(二:配置安装包依赖项和自定义dll)...
- Chrome 开发者工具 live expression 的用法
- 休眠锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作
- excel单元格斜线_掌握这20个Excel技巧,小白轻松变大神
- 狂神说Java学习笔记 Java基础
- Rust学习笔记——模式和匹配
- 多个高危 BIOS 缺陷影响英特尔处理器,特斯拉 Model 3 未幸免,可用于供应链攻击...
- linux 安装tomcat
- 如何总结和整理学术文献?
- oracle扩容temp表空间,解决oracle临时表空间的报错
- 初看SOA:SOA是什么?
- PhantomJS+Selenium爬取淘宝
- ubuntu安装迅雷
- Spring下载教程(保姆级)
- 积极推进市域治理现代化,全力打造国家治理体系新局面