音乐播放器
一个小Demo
功能实现:列表页面和播放页面
知识点:歌词拆分 --- 把一首歌词拆分 --- 每一句拆分为一个模型 --- 所有的模型放到数组中 --- 工具类(单例)--- 私有属性
// 歌词拆分 -(void)lyricArrayWith:(BCCMusic *)model { // 数组清零 [self.allDataArray removeAllObjects]; NSString *lyricStr = model.lyric; // 每行歌词 NSArray *array = [lyricStr componentsSeparatedByString:@"\n"]; for (NSString *str in array) { NSArray *timeAndStrArray = [str componentsSeparatedByString:@"]"]; if ([timeAndStrArray.firstObject length] < 1) { break; } NSString *timeStr = [timeAndStrArray.firstObject substringFromIndex:1]; NSString *minutes = [timeStr componentsSeparatedByString:@":"].firstObject; NSString *seconds = [timeStr componentsSeparatedByString:@":"].lastObject; // 时间 NSTimeInterval time = [minutes floatValue]*60 + [seconds floatValue]; // 歌词 NSString *smallLyricStr = [timeAndStrArray lastObject]; // 模型 BCCLyric *smallLyric = [BCCLyric lyricWithStr:smallLyricStr time:time]; [self.allDataArray addObject:smallLyric]; } }
***工具类的作用:通过工具类的实例对象或者工具类 对应的方法 --- 达到相应的效果(目的)
***播放管理工具类
@class BCCPlayerTools; @protocol BCCPlayerToolsDelegate <NSObject> #pragma mark -声明代理方法 - (void)musicPlayingEndToTimeAction; - (void)player:(BCCPlayerTools *)playTool playingWithProgress:(NSTimeInterval)progress; @end @interface BCCPlayerTools : NSObject @property(nonatomic,assign)BOOL isPlaying; @property(nonatomic,assign)NSInteger volume; @property(nonatomic,assign)id<BCCPlayerToolsDelegate> delegate; + (instancetype)shareBCCPlayerTools; #pragma mark -方法声明 // 准备 - (void)preparePlayingWith:(NSString *)mp3UrlString; // 播放 - (void)play; // 暂停 - (void)pause; // 根据指定时间播放 - (void)seekToTime:(NSTimeInterval)time; @end #import "BCCPlayerTools.h" #import <AVFoundation/AVFoundation.h> @interface BCCPlayerTools () // //{ // BOOL _isPrepare; // BOOL _isPlaying; //} @property(nonatomic,strong)AVPlayer *player; @property(nonatomic,strong)NSTimer *timer; @end @implementation BCCPlayerTools + (instancetype)shareBCCPlayerTools { static BCCPlayerTools *playerTool = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ playerTool = [[BCCPlayerTools alloc] init]; }); return playerTool; } - (instancetype)init { self = [super init]; if (self) { // 初始化播放器 self.player = [[AVPlayer alloc] init]; // 添加通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playingToEndAction:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; } return self; } #pragma mark -通知响应的方法 - (void)playingToEndAction:(NSNotification *)sender { if (self.delegate && [self.delegate respondsToSelector:@selector(musicPlayingEndToTimeAction)]) { [self.delegate musicPlayingEndToTimeAction]; } } #pragma mark -方法声明 // 准备 - (void)preparePlayingWith:(NSString *)mp3UrlString { if (self.player.currentItem) { // 移除 [self.player removeObserver:self forKeyPath:@"status"]; } AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:mp3UrlString]]; [self.player replaceCurrentItemWithPlayerItem:item]; // [self.player play]; // KVO [self.player addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"status"]) { if (self.player.status == AVPlayerStatusReadyToPlay) { [self play]; } } } // 播放 - (void)play { // 先暂停 [self pause]; _isPlaying = YES; [self.player play]; if (self.timer) { return; } // 启动定时器 self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerResponseAction:) userInfo:nil repeats:YES]; } // 暂停 - (void)pause { if (!_isPlaying) { return; } [self.player pause]; _isPlaying = NO; // 状态置NO // 暂停后把定时器取消并置空 [self.timer invalidate]; self.timer = nil; } // 根据指定时间播放 - (void)seekToTime:(NSTimeInterval)time { // 先暂停 [self pause]; [self.player seekToTime:CMTimeMakeWithSeconds(time, self.player.currentTime.timescale) completionHandler:^(BOOL finished) { if (finished) { [self play]; } }]; } // 定时器响应方法 - (void)timerResponseAction:(NSTimer *)sender { if (self.delegate && [self.delegate respondsToSelector:@selector(player:playingWithProgress:)]) { // 把当前播放器时间传出去 CGFloat progress = self.player.currentTime.value / self.player.currentTime.timescale; [self.delegate player:self playingWithProgress:progress]; } } // 重写volume的setter方法 - (void)setVolume:(NSInteger)volume { self.player.volume = (CGFloat)volume; } - (BOOL)isPlaying { return _isPlaying; } @end
*** 音乐管理工具类
typedef void (^RefreshBlock) (); @class BCCMusic; @interface BCCMusicTools : NSObject @property(nonatomic,assign,readonly)NSInteger count; @property(nonatomic,copy)RefreshBlock block; + (instancetype)shareMusicToos; // 获取对应的模型 - (BCCMusic *)musicWithIndex:(NSInteger)index; @end #import "BCCMusicTools.h" #import "BCCMusic.h" #import "BCCURLs.h" @interface BCCMusicTools () @property(nonatomic,strong)NSMutableArray *musicListArray; @end @implementation BCCMusicTools + (instancetype)shareMusicToos { static BCCMusicTools *musicTool; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ musicTool = [[BCCMusicTools alloc] init]; [musicTool urlRequest]; }); return musicTool; } - (void)urlRequest { dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 耗时操作 NSArray *array = [NSArray arrayWithContentsOfURL:[NSURL URLWithString:kMusicListURL]]; for (NSDictionary *dict in array) { BCCMusic *model = [[BCCMusic alloc] init]; [model setValuesForKeysWithDictionary:dict]; [self.musicListArray addObject:model]; } dispatch_async(dispatch_get_main_queue(), ^{ // 调用block --- 以后回调 self.block(); }); }); } // 获取对应的模型 - (BCCMusic *)musicWithIndex:(NSInteger)index { return self.musicListArray[index]; } // 懒加载 - (NSMutableArray *)musicListArray { if (!_musicListArray) { _musicListArray = [NSMutableArray new]; } return _musicListArray; } // 重写getter方法 - (NSInteger)count { return self.musicListArray.count; } @end
***歌词管理工具类
@class BCCMusic; @interface BCCLyricTools : NSObject @property(nonatomic,assign)NSInteger count; + (instancetype)shareBCCLyricTools; #pragma mark -声明方法 // 歌词拆分 - (void)lyricArrayWith:(BCCMusic *)model; // 根据下标 --- 拿到歌词 - (NSString *)stringWithIndex:(NSInteger)index; // 通过时间 --- 拿到下标 - (NSInteger)indexWithTime:(NSTimeInterval)time; @end #import "BCCLyricTools.h" #import "BCCMusic.h" #import "BCCLyric.h" @interface BCCLyricTools () #pragma mark -私有属性 @property(nonatomic,strong)NSMutableArray *allDataArray; @end @implementation BCCLyricTools + (instancetype)shareBCCLyricTools { static BCCLyricTools *lyricTool = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ lyricTool = [[BCCLyricTools alloc] init]; }); return lyricTool; } #pragma mark -声明方法 // 歌词拆分 -(void)lyricArrayWith:(BCCMusic *)model { // 数组清零 [self.allDataArray removeAllObjects]; NSString *lyricStr = model.lyric; // 每行歌词 NSArray *array = [lyricStr componentsSeparatedByString:@"\n"]; for (NSString *str in array) { NSArray *timeAndStrArray = [str componentsSeparatedByString:@"]"]; if ([timeAndStrArray.firstObject length] < 1) { break; } NSString *timeStr = [timeAndStrArray.firstObject substringFromIndex:1]; NSString *minutes = [timeStr componentsSeparatedByString:@":"].firstObject; NSString *seconds = [timeStr componentsSeparatedByString:@":"].lastObject; // 时间 NSTimeInterval time = [minutes floatValue]*60 + [seconds floatValue]; // 歌词 NSString *smallLyricStr = [timeAndStrArray lastObject]; // 模型 BCCLyric *smallLyric = [BCCLyric lyricWithStr:smallLyricStr time:time]; [self.allDataArray addObject:smallLyric]; } } // 根据下标 --- 拿到歌词 - (NSString *)stringWithIndex:(NSInteger)index { BCCLyric *model = self.allDataArray[index]; return model.str; } // 通过时间 --- 拿到下标 - (NSInteger)indexWithTime:(NSTimeInterval)time { for (int i = 0;i < self.allDataArray.count;i++) { BCCLyric *model = self.allDataArray[i]; if (time < model.time) { return (i -1)>0?(i-1):0; } } return 0; } // 重写getter方法 - (NSInteger)count { return self.allDataArray.count; } // 懒加载 - (NSMutableArray *)allDataArray { if (!_allDataArray) { _allDataArray = [NSMutableArray array]; } return _allDataArray; } @end
***播放详情页面Controller
@interface BCCMusicPlayingViewController : UIViewController @property(nonatomic,assign)NSInteger index; + (instancetype)shareBCCMusicPlayingViewController; @end #import "BCCMusicPlayingViewController.h" #import "BCCPlayerTools.h" #import "BCCMusicTools.h" #import "BCCMusic.h" #import "BCCLyricTools.h" #import "UIImageView+WebCache.h" @interface BCCMusicPlayingViewController ()<BCCPlayerToolsDelegate,UITableViewDataSource,UITableViewDelegate> { // 当前正在播放的下标 NSInteger _currentIndex; } @property (strong, nonatomic) IBOutlet UILabel *musicNameLabel; @property (strong, nonatomic) IBOutlet UILabel *musicSingerLabel; @property (strong, nonatomic) IBOutlet UIImageView *musicImageView; @property (strong, nonatomic) IBOutlet UISlider *timeSlider; @property (strong, nonatomic) IBOutlet UISlider *volumeSlider; @property (strong, nonatomic) IBOutlet UILabel *currentTimeLabel; @property (strong, nonatomic) IBOutlet UILabel *allTimeLabel; @property (strong, nonatomic) IBOutlet UITableView *lyricTableView; @property (strong, nonatomic) IBOutlet UIButton *stopAndStartButton; - (IBAction)timeSliderAction:(UISlider *)sender; - (IBAction)volumeSliderActon:(UISlider *)sender; @end @implementation BCCMusicPlayingViewController + (instancetype)shareBCCMusicPlayingViewController { static BCCMusicPlayingViewController *musicPlayingVC; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ musicPlayingVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"BCCMusicPlayingViewController"]; }); return musicPlayingVC; } - (void)viewDidLoad { [super viewDidLoad]; _currentIndex = -1; // 设置代理 [BCCPlayerTools shareBCCPlayerTools].delegate = self; self.lyricTableView.dataSource = self; self.lyricTableView.delegate = self; // 注册cell [self.lyricTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"lyricCell"]; self.lyricTableView.separatorStyle = NO; // 声音slider self.volumeSlider.maximumValue = 100; // 声音默认为2 self.volumeSlider.value = 2.0; // 设置圆形图片 self.musicImageView.layer.masksToBounds = YES; self.musicImageView.layer.cornerRadius = 125; [self.timeSlider setThumbImage:[UIImage imageNamed:@"thumb"] forState:UIControlStateNormal]; [self.volumeSlider setThumbImage:[UIImage imageNamed:@"thumb"] forState:UIControlStateNormal]; } -(void)viewWillAppear:(BOOL)animated { // 开始播放 [self startPlaying]; } #pragma mark - 开始播放 - (void)startPlaying { if (_currentIndex == _index) { return; } _currentIndex = _index; [self playingWithIndex:_currentIndex]; } #pragma mark 根据下标播放 - (void)playingWithIndex:(NSInteger)index { // 先暂停 --- 重要 [[BCCPlayerTools shareBCCPlayerTools] pause]; BCCMusic *music = [[BCCMusicTools shareMusicToos] musicWithIndex:index]; [[BCCPlayerTools shareBCCPlayerTools] preparePlayingWith:music.mp3Url]; // 总时间 self.allTimeLabel.text = [self returnStringWithTime:[music.duration doubleValue]/1000]; self.timeSlider.maximumValue = [music.duration doubleValue]/1000; // 工具拆分歌词 [[BCCLyricTools shareBCCLyricTools] lyricArrayWith:music]; [self.lyricTableView reloadData]; // 更新 self.musicNameLabel.text = music.name; self.musicSingerLabel.text = music.singer; [self.musicImageView sd_setImageWithURL:[NSURL URLWithString:music.picUrl] placeholderImage:[UIImage imageNamed:@"01.jpg"]]; // button默认图片 [self.stopAndStartButton setBackgroundImage:[UIImage imageNamed:@"pause.png"] forState:UIControlStateNormal]; [self stopAndStartButtonDidClicked:nil]; } #pragma mark -BCCPlayerToolsDelegate代理方法 // 代理方法 --- BCCPlayerToolsDelegate --- 自动下一首 -(void)musicPlayingEndToTimeAction { [self nextMusicButtonDidClicked:nil]; } -(void)player:(BCCPlayerTools *)playTool playingWithProgress:(NSTimeInterval)progress { // 动画 --- 旋转 self.musicImageView.transform = CGAffineTransformRotate(_musicImageView.transform, M_PI / 180); // 一直被执行的代理方法(定时器) // 当前时间 self.currentTimeLabel.text = [self returnStringWithTime:progress]; self.timeSlider.value = progress; NSInteger index = [[BCCLyricTools shareBCCLyricTools] indexWithTime:progress]; [self.lyricTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle]; } #pragma mark -秒数转00:00格式的字符串 - (NSString *)returnStringWithTime:(NSTimeInterval)time { NSInteger minutes = time/60; NSInteger seconds = (NSInteger)time%60; NSString *str = [NSString stringWithFormat:@"%02ld:%02ld",(long)minutes,(long)seconds]; return str; } #pragma mark -stroyboard关联事件 // 上一首 - (IBAction)beforeMusicButtonDidClicked:(UIButton *)sender { _currentIndex --; if (_currentIndex < 0) { _currentIndex = [BCCMusicTools shareMusicToos].count - 1; } _index = _currentIndex; [self playingWithIndex:_currentIndex]; } // 下一首 - (IBAction)nextMusicButtonDidClicked:(UIButton *)sender { _currentIndex ++; if (_currentIndex > [BCCMusicTools shareMusicToos].count - 1) { _currentIndex = 0; } _index = _currentIndex; [self playingWithIndex:_currentIndex]; } // 暂停-开始 - (IBAction)stopAndStartButtonDidClicked:(UIButton *)sender { NSLog(@"%d",[BCCPlayerTools shareBCCPlayerTools].isPlaying); if ([BCCPlayerTools shareBCCPlayerTools].isPlaying) { [[BCCPlayerTools shareBCCPlayerTools] pause]; [self.stopAndStartButton setBackgroundImage:[UIImage imageNamed:@"play.png"] forState:UIControlStateNormal]; }else{ [[BCCPlayerTools shareBCCPlayerTools] play]; [self.stopAndStartButton setBackgroundImage:[UIImage imageNamed:@"pause.png"] forState:UIControlStateNormal]; } } - (IBAction)cancelButtonDidClicked:(UIButton *)sender { [self dismissViewControllerAnimated:YES completion:nil]; } // 时间滑竿 - (IBAction)timeSliderAction:(UISlider *)sender { // 根据slider的value播放 --- 指定位置播放 [[BCCPlayerTools shareBCCPlayerTools] seekToTime:sender.value]; } // 声音滑竿 - (IBAction)volumeSliderActon:(UISlider *)sender { [BCCPlayerTools shareBCCPlayerTools].volume = sender.value; } /*******************************lyricTableView的代理方法***********************************/ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [BCCLyricTools shareBCCLyricTools].count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"lyricCell" forIndexPath:indexPath]; cell.textLabel.text = [[BCCLyricTools shareBCCLyricTools] stringWithIndex:indexPath.row]; // 文字居中 cell.textLabel.textAlignment = NSTextAlignmentCenter; cell.textLabel.highlightedTextColor = [UIColor redColor]; cell.selectedBackgroundView = ({ UIView *view = [[UIView alloc] initWithFrame:cell.frame]; view.backgroundColor = [UIColor clearColor]; view; }); return cell; } @end
***列表展示页面
@interface BCCMusicListTableViewController : UITableViewController @end #import "BCCMusicListTableViewController.h" #import "BCCMusicListTableViewCell.h" #import "BCCMusicTools.h" #import "BCCMusicPlayingViewController.h" #import "MBProgressHUD.h" @interface BCCMusicListTableViewController ()<UITableViewDataSource,UITableViewDelegate> @end @implementation BCCMusicListTableViewController - (void)viewDidLoad { [super viewDidLoad]; // tableview注册cell [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([BCCMusicListTableViewCell class]) bundle:nil] forCellReuseIdentifier:@"BCCMusicListTableViewCell"]; self.tableView.dataSource = self; self.tableView.delegate = self; [BCCMusicTools shareMusicToos].block = ^(){ [self.tableView reloadData]; }; [self hudload]; } #pragma mark -小菊花加载 - (void)hudload { MBProgressHUD *hud = [[MBProgressHUD alloc] initWithFrame:self.view.frame]; [self.view addSubview:hud]; hud.dimBackground = YES; hud.labelText = @"加载中..."; [hud showWhileExecuting:@selector(hudSleep) onTarget:self withObject:nil animated:YES]; } - (void)hudSleep { sleep(2); } #pragma mark - TableView代理方法 // 行数rows - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // NSLog(@"%ld",[BCCMusicTools shareMusicToos].count); return [BCCMusicTools shareMusicToos].count; } // cell设置 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { BCCMusicListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BCCMusicListTableViewCell" forIndexPath:indexPath]; BCCMusic *model = [[BCCMusicTools shareMusicToos] musicWithIndex:indexPath.row]; [cell initWithMusic:model]; // NSLog(@"%@",model.lyric); return cell; } // 行高 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 100; } // cell点击事件 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 取消选中状态 --- UITableViewController --- 自动取消选中状态 BCCMusicPlayingViewController *musicPlayingVC = [BCCMusicPlayingViewController shareBCCMusicPlayingViewController]; // 传值 musicPlayingVC.index = indexPath.row; [self showDetailViewController:musicPlayingVC sender:nil]; } // BarButtonItem关联事件 - (IBAction)rightBarButtonDidClicked:(UIBarButtonItem *)sender { BCCMusicPlayingViewController *musicPlayingVC = [BCCMusicPlayingViewController shareBCCMusicPlayingViewController]; [self showDetailViewController:musicPlayingVC sender:nil]; } @end