ios 简单的本地音乐播放器

一、导入资源文件

二、新建一个控制器,继承于UITableViewController,用来展示播放列表

1、播放列表的实现

@interface MusicListViewController (){
    //定义一个播放列表数组
    NSMutableArray *musicList;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    //调用解析文件类方法,得到播放列表
    musicList=[MusicModel allMusics];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    MusicModel *model=musicList[indexPath.row];
    //调用画图的分类方法,使歌手头像变成圆形
    cell.imageView.image=[UIImage circleImageWithName:model.singerIcon borderWidth:5 borderColor:[UIColor redColor]];
    cell.textLabel.text=model.name;
    
    return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 80;
}
//点击了对应的cell后执行下面代码
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    MusicModel *model=musicList[indexPath.row];
    [self.playerVC showWithPlayingMusic:model];
}

2、添加一个UIimageView的分类,调整歌手的头像(正方形——>圆形)

  分类的实现代码如下:

  UIImage+YY.h文件

+(instancetype)circleImageWithName:(NSString *)name borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor{
//1、拿到要处理的图片
    UIImage *oldImg=[UIImage imageNamed:name];
//2、你要绘图,首先要创建上下文(你可以指定上下文的区域)
    CGFloat imgW=oldImg.size.width+borderWidth*2;
    CGFloat imgH=oldImg.size.height+borderWidth*2;
    UIGraphicsBeginImageContext(CGSizeMake(imgW, imgH));
//3、拿到所创建的上下文,设置填充颜色
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    [borderColor set];
//4、画大圆(边框颜色以及宽度)
    CGFloat radius=imgW/2.0;//大圆的半径
    CGFloat cenX=radius;
    CGFloat cenY=radius;
    CGContextAddArc(ctx, cenX, cenY, radius, 0, 2*M_PI, 0);
    CGContextFillPath(ctx);
//5、画小圆(把图片画上去)
    CGFloat smallRadius=radius-borderWidth;
    CGContextAddArc(ctx, cenX, cenY, smallRadius, 0, 2*M_PI, 0);
    //裁剪
    CGContextClip(ctx);
//6、画图
    [oldImg drawInRect:CGRectMake(borderWidth, borderWidth, oldImg.size.width, oldImg.size.height)];
//7、从当前上下文中拿到一张新图片
    UIImage *newImage=UIGraphicsGetImageFromCurrentImageContext();
//8、关闭上下文
    UIGraphicsEndImageContext();
    return newImage;
}

3、解析歌曲信息,获得歌曲模型

xxxx.h

@interface MusicModel : NSObject
//歌曲名称
@property(nonatomic,strong)NSString *name;
//歌曲链接
@property(nonatomic,strong)NSString *filename;
//歌词名称
@property(nonatomic,strong)NSString *lrcname;
//歌手
@property(nonatomic,strong)NSString *singer;
//歌手头像
@property(nonatomic,strong)NSString *singerIcon;
//封面照片
@property(nonatomic,strong)NSString *icon;

//返回所有音乐模型
+(NSMutableArray *)allMusics;

@end

xxxx.m

-(instancetype)initWithDict:(NSDictionary *)dict{
    if (self=[super init]) {
        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}

//返回了所有音乐模型
+(NSMutableArray *)allMusics{
    NSMutableArray *arrM=[[NSMutableArray alloc]init];
    NSString *path=[[NSBundle mainBundle]pathForResource:@"Musics.plist" ofType:nil];
    NSArray *tmpArr=[NSArray arrayWithContentsOfFile:path];
    for (NSDictionary *tmpDic in tmpArr) {
        MusicModel *model=[[MusicModel alloc]initWithDict:tmpDic];
        [arrM addObject:model];
    }
    return arrM;
}

三、点击了对应的歌曲后,会跳转到另一个控制器:这里用xib做,继承与UIViewController

界面部分如下:

在上一个播放列表界面,点击对应歌曲后执行的方法如下:

@interface MusicPlayerController : UIViewController

@property(nonatomic,strong)NSArray *musicList;
@property(nonatomic,strong)AVAudioPlayer *player;
@property(nonatomic,strong)MusicModel *playIngModel;//当前正在播放的音乐模型

//写一个方法来显示界面
-(void)showWithPlayingMusic:(MusicModel *)model;

@end
@interface MusicPlayerController ()<AVAudioPlayerDelegate>{
    NSTimer *t;
    LrcView *lrcView;
}
//返回到上一层界面
- (IBAction)exitBtnPressed:(id)sender;
//上一曲按钮
- (IBAction)previousBtnPressed:(id)sender;
//播放暂停按钮
- (IBAction)playBtnPressed:(id)sender;
//下一曲按钮
- (IBAction)nextBtnPressed:(id)sender;
//进度条
@property (weak, nonatomic) IBOutlet UISlider *slider;
- (IBAction)sliderChanged:(id)sender;
@property (weak, nonatomic) IBOutlet UILabel *songName;
@property (weak, nonatomic) IBOutlet UILabel *singer;
@property (weak, nonatomic) IBOutlet UIImageView *imgView;
@end

 

这里既不用push也不用model,因为当返回到上一个界面的时候不能让这个界面销毁

//显示界面
-(void)showWithPlayingMusic:(MusicModel *)model{
//1、拿到window
    UIWindow *window=[UIApplication sharedApplication].windows[0];
//2、将当前控制器的view添加到window上面
    [window addSubview:self.view];
//3、指定self.view的frame
    self.view.frame=CGRectMake(0, WinHeight, WinWidth, WinHeight);
//4、动画,让当前view从下方显示出来
    [UIView animateWithDuration:1.0 animations:^{
        self.view.frame=window.frame;
    }];
    
//5、播放音乐
    //当前所播放的音乐和外面传进来的音乐不一样的时候,才需要重新播放
    if (![self.playIngModel isEqual:model]) {
        [self resertPlayingModel:model];
    }
}

//重写PlayingModel方法(当前播放歌曲改变调用此方法)
-(void)resertPlayingModel:(MusicModel *)model{
    _playIngModel=model;
  
    self.imgView.image=[UIImage imageNamed:model.icon];
    //播放音乐
    NSURL *url=[[NSBundle mainBundle]URLForResource:model.filename withExtension:nil];
    //声明会话类型,后台播放步骤2
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setActive:YES error:nil];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    self.player=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
    self.player.delegate=self;
    [self.player prepareToPlay];
    [self.player play];
    //指定silder的信息
    self.slider.minimumValue=0;
    self.slider.value=0;
    self.slider.maximumValue=self.player.duration;
    
    self.songName.text=model.name;
    self.singer.text=model.singer;
    
    t=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateSlider:) userInfo:nil repeats:YES];
}
//刷新slider的进度
-(void)updateSlider:(NSTimer *)t{
    self.slider.value=self.player.currentTime;
}

- (IBAction)sliderChanged:(id)sender {
    self.player.currentTime=self.slider.value;
}


- (IBAction)playBtnPressed:(id)sender {
    UIButton *btn=sender;
    btn.selected=!btn.selected;
    //如果当前播放器正在播放音乐,则暂停
    if ([self.player isPlaying]) {
        [self.player pause];
    }
    //否则继续播放
    else{
        [self.player play];
    }
}

- (IBAction)previousBtnPressed:(id)sender {
    //1、拿到当前正在播放的音乐对应数组的index
    NSInteger currentIndex=[self.musicList indexOfObject:_playIngModel];
    //2、让currentIndex--
    currentIndex--;
    if (currentIndex<0) {
        currentIndex=self.musicList.count-1;
    }
    MusicModel *model=self.musicList[currentIndex];
    [self resertPlayingModel:model];
}

- (IBAction)nextBtnPressed:(id)sender {
    //1、拿到当前正在播放的音乐对应数组的index
    NSInteger currentIndex=[self.musicList indexOfObject:_playIngModel];
    //2、让currentIndex++
    currentIndex++;
    if (currentIndex>=self.musicList.count) {
        currentIndex=0;
    }
    MusicModel *model=self.musicList[currentIndex];
    [self resertPlayingModel:model];
}

//显示界面
-(void)showWithPlayingMusic:(MusicModel *)model{
//1、拿到window
    UIWindow *window=[UIApplication sharedApplication].windows[0];
//2、将当前控制器的view添加到window上面
    [window addSubview:self.view];
//3、指定self.view的frame
    self.view.frame=CGRectMake(0, WinHeight, WinWidth, WinHeight);
//4、动画,让当前view从下方显示出来
    [UIView animateWithDuration:1.0 animations:^{
        self.view.frame=window.frame;
    }];
    
//5、播放音乐
    //当前所播放的音乐和外面传进来的音乐不一样的时候,才需要重新播放
    if (![self.playIngModel isEqual:model]) {
        [self resertPlayingModel:model];
    }
}

- (IBAction)exitBtnPressed:(id)sender {
    [UIView animateWithDuration:1.0 animations:^{
        self.view.frame=CGRectMake(0, WinHeight, WinWidth, WinHeight);
    }];
}

#pragma mark AVAudioPlayerDelegate
//当播放完一首歌之后自动跳转到下一首
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    [t invalidate];
    [self nextBtnPressed:nil];
}

五、歌词部分 :这里把它分装出来了,xib方式,继承于UIView

往控件上托一个UITableView,继承于UITableViewDataSource,UITableViewDelegate

首次先传递歌词界面及信息:

-(void)viewDidLoad{
    [super viewDidLoad];
    //初始化显示歌词的view
    lrcView=[LrcView lrcView];
    [self.view insertSubview:lrcView aboveSubview:self.imgView];
}

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    lrcView.frame=CGRectMake(0, 0, WinWidth, WinHeight-100);
    lrcView.backgroundColor=[UIColor clearColor];
}

//重写PlayingModel方法(当前播放歌曲改变调用此方法)
-(void)resertPlayingModel:(MusicModel *)model{
    _playIngModel=model;
  
    self.imgView.image=[UIImage imageNamed:model.icon];
    //播放音乐
    NSURL *url=[[NSBundle mainBundle]URLForResource:model.filename withExtension:nil];
    
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setActive:YES error:nil];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    self.player=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
    self.player.delegate=self;
    [self.player prepareToPlay];
    [self.player play];
    
    //传递歌词信息
    lrcView.lrcName=model.lrcname;
    lrcView.player=self.player;

//指定silder的信息
    self.slider.minimumValue=0;
    self.slider.value=0;
    self.slider.maximumValue=self.player.duration;
    
    self.songName.text=model.name;
    self.singer.text=model.singer;
    
    t=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateSlider:) userInfo:nil repeats:YES];
}

 

@interface LrcView : UIView<UITableViewDataSource,UITableViewDelegate>{
    NSTimer *t;
}
@property (weak, nonatomic) IBOutlet UITableView *tableView;


@property(nonatomic,assign)NSInteger selectIndex;
//当前所播放的歌词的名称
@property(nonatomic,strong)NSString *lrcName;
@property(nonatomic,strong)AVAudioPlayer *player;
+(instancetype)lrcView;

@property(nonatomic,strong)NSMutableArray *contentList;

@end
#import "LrcView.h"
#import "LrcLine.h"

@implementation LrcView

-(void)awakeFromNib{
    //取消tableView的分割线
    self.tableView.separatorStyle=UITableViewCellSeparatorStyleNone;
    self.tableView.contentInset=UIEdgeInsetsMake(self.tableView.frame.size.height*0.5, 0, 0, 0);
}

-(void)layoutSubviews{
    [super layoutSubviews];
}

//拿到歌词名称了
-(void)setLrcName:(NSString *)lrcName{
    _lrcName=lrcName;
    //初始化一个歌词名(10405520.lrc)对应的所有歌词信息
    _contentList=[LrcLine allLrcLineModelsWithFileName:lrcName];
    [self.tableView reloadData];
    
    //每隔0.5秒执行刷新歌词的方法
    t=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateLrc) userInfo:nil repeats:YES];
}

-(void)updateLrc{
    //当前播放器的时间
    CGFloat currentTime=self.player.currentTime;//80.89秒
    int minute=currentTime/60;
    int second=(int)currentTime%60;
    int msecond=(currentTime-(int)currentTime)*100;
    //把当前播放器的播放时间转换为字符串
    NSString *currentTimeStr=[NSString stringWithFormat:@"%02d:%02d.%02d",minute,second,msecond];
    
    //遍历每一个歌词
    for (int i=0; i<self.contentList.count; i++) {
        LrcLine *model=self.contentList[i];
        NSInteger nextIndex=i+1;
        //最后一个歌词
        if (nextIndex<self.contentList.count) {
            LrcLine *nextModel=self.contentList[nextIndex];
            
            if ([currentTimeStr compare:model.time]!=NSOrderedAscending &&[currentTimeStr compare:nextModel.time]==NSOrderedAscending &&_selectIndex!=i) {//_selectIndex!=i 第一次滚动到对应的行后,定时器0.5s一刷新,tableView在第i行结束前就不用重复滚动和刷新了
                
                //刷新指定行的内容
                NSArray *reloadRows=@[[NSIndexPath indexPathForRow:_selectIndex inSection:0],[NSIndexPath indexPathForRow:i inSection:0]];
                _selectIndex=i;
                [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationNone];
                
                //满足条件,就让tableView滚动到对应的行
                NSIndexPath *indexPath=[NSIndexPath indexPathForRow:i inSection:0];
                [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
            }
        }
    }
}

+(instancetype)lrcView{
    return [[[NSBundle mainBundle]loadNibNamed:@"LrcView" owner:nil options:nil] lastObject];
}

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

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *identifier=@"cell";
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:identifier];
    if (cell==nil) {
        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
        cell.backgroundColor=[UIColor clearColor];
        cell.selectionStyle=UITableViewCellSelectionStyleNone;
        cell.textLabel.textColor=[UIColor whiteColor];
        cell.textLabel.textAlignment=NSTextAlignmentCenter;
    }
    if (indexPath.row==_selectIndex) {
        cell.textLabel.textColor=[UIColor redColor];
        cell.textLabel.font=[UIFont systemFontOfSize:20];
    }
    else{
        cell.textLabel.textColor=[UIColor whiteColor];
        cell.textLabel.font=[UIFont systemFontOfSize:16];
    }
    LrcLine *model=self.contentList[indexPath.row];
    cell.textLabel.text=model.word;
    return cell;
}

@end

解析歌词:

//返回一个歌词名对应的所有的model信息
+(NSMutableArray *)allLrcLineModelsWithFileName:(NSString *)lrcName{
    NSMutableArray *arrM=[[NSMutableArray alloc]init];
    NSURL *url=[[NSBundle mainBundle]URLForResource:lrcName withExtension:nil];
    //把歌词信息转换字符串
    NSString *lrcStr=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
    NSArray *lrcComps=[lrcStr componentsSeparatedByString:@"\n"];
    //转换成model
    for (NSString *tmpStr in lrcComps) {
        LrcLine *model=[[LrcLine alloc]init];
        [arrM addObject:model];
        
        if (![tmpStr hasPrefix:@"["]) {
            continue ;//跳出当前循环一次
        }
        if ([tmpStr hasPrefix:@"[ti:"] ||[tmpStr hasPrefix:@"[ar:"]||[tmpStr hasPrefix:@"[al:"]) {
            NSString *word=[[tmpStr componentsSeparatedByString:@":"] lastObject];
            model.word=[word substringToIndex:word.length-1];
        }
        else{
            NSArray *array=[tmpStr componentsSeparatedByString:@"]"];
            model.time=[[array firstObject] substringFromIndex:1];
            model.word=[array lastObject];
        }
    }
    return arrM;
}

六、后台播放:

开启后台模式步骤
 1、infoPlist  
    添加  Required background modes  说明要支持的后台类型
 2、要支持音乐的后台播放,需要声明会话类型
     AVAudioSession *session = [AVAudioSession sharedInstance];
     [session setActive:YES error:nil];
     [session setCategory:AVAudioSessionCategoryPlayback error:nil];
 3、添加代码
     -(BOOL)canBecomeFirstResponder{
         return YES;
     }
     
     //远程控制器事件
     -(void)remoteControlReceivedWithEvent:(UIEvent *)event{
     
     }
 4、注册对应的通知,并且实现通知规定的方法
     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(willResignActive) name:UIApplicationWillResignActiveNotification object:nil];
     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(willEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
 
     -(void)willResignActive{
         [self becomeFirstResponder];
         //开始接受远程控制事件
         [[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
     }
     //当应用程序将要进入前台调用该方法
     -(void)willEnterForeground{
         [self resignFirstResponder];
         //取消接受远程控制事件
         [[UIApplication sharedApplication]endReceivingRemoteControlEvents];
     }
//远程控制器事件
-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
    if (event.type==UIEventTypeRemoteControl) {
        switch (event.subtype) {
            case UIEventSubtypeMotionShake:// 摇晃事件
                break;
            case UIEventSubtypeRemoteControlPause://暂停事件
                [_player pause];
                break;
            case UIEventSubtypeRemoteControlPlay://播放事件
                [_player play];
                break;
            case UIEventSubtypeRemoteControlStop://停止事件
                break;
                //播放或暂停切换【操作:播放或暂停状态下,按耳机线控中间按钮一下】
            case UIEventSubtypeRemoteControlTogglePlayPause:
                if ([_player isPlaying]) {
                    [_player pause];
                }
                else{
                    [_player play];
                }
                break;
            case UIEventSubtypeRemoteControlNextTrack://下一曲
                [self nextBtnPressed:nil];;
                break;
            case UIEventSubtypeRemoteControlPreviousTrack://上一曲
                [self previousBtnPressed:nil];
                break;
            case UIEventSubtypeRemoteControlBeginSeekingForward://快进开始
                NSLog(@"Begin seek forward...");
                break;
            case UIEventSubtypeRemoteControlEndSeekingForward://快进结束
                NSLog(@"End seek forward...");
                break;
            case UIEventSubtypeRemoteControlBeginSeekingBackward://快退开始
                NSLog(@"Begin seek backward...");
                break;
            case UIEventSubtypeRemoteControlEndSeekingBackward://快退结束
                NSLog(@"End seek backward...");
                break;
            default:
                break;
        }
    }
}

 

posted @ 2015-12-27 22:52  将心放逐  阅读(1856)  评论(0编辑  收藏  举报