音乐播放器

一个小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

 

posted @ 2015-08-09 23:36  百川hl  阅读(286)  评论(0编辑  收藏  举报