• iOS 开发仿网易云音乐歌词海报


    这里写图片描写叙述


    使用网易云音乐也是一个巧合,我之前一直使用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:@"
    "]){
                        [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 imageView
        self.imageView = [UIImageView new];
    
        // resize the background image , adapte to the lrcs
        self.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 加入分享与保存button
     */
    - (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 array
            NSString *lrc = [lrcsArray objectAtIndex:i];
            int lrcLen = lrc.length;
    
            //create a label to show the lrc
            UILabel *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 name
        NSString *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


    好了。

    祝大家生活愉快。多多收获友谊和爱情。假设想获取很多其它的讯息,请扫描下方二维码关注我的微信公众号:

    这里写图片描写叙述

  • 相关阅读:
    layer子层给父层页面元素赋值,以达到向父层页面传值的效果
    根据HttpServletRequest获取用户真实IP地址
    KVM--安装及初步使用
    eslint 踩坑 -- error '***' is assigned a value but never used no-unused-vars
    eslint 效验规则
    leetcode407接雨水II
    leetcodej剑指offer41.数据流中的中位数
    leetcode44通配符匹配
    leetcode955K连续位的最小反转次数
    leetcode638大礼包
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8452230.html
Copyright © 2020-2023  润新知