iOS 类似于音乐播放器的FM

本人不才,在项目中写了个FM,也类似于播放器吧!为了减少代码,我封装了几个类来解决这些问题。写音乐播放器首先你要想你需要那几个类。音乐播放器你得需要,向客户展示播放暂停等View你的需要吧,在此我又封装了一个解析数据的类。废话不多说开始吧! 向大家展示一下写项目之前调研的界面吧!!

一.首先我们先写音乐播放器类(JYJMusicPlayerTools) 1..h中

class JYJProgramModel;
@protocol JYJMusicPlayerToolsDelegate <NSObject>
// 通过代理返回当前歌曲播放的进度
- (void)getCurTime:(NSString *)curTime totle:(NSString *)totle progress:(CGFloat)progress;
//  播放结束后,如何操作由外部决定
- (void)endPlayAction;
//播放失败后
- (void)playFail;
@end
@interface JYJMusicPlayerTools : NSObject
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerItem *playerItem;
@property (nonatomic, strong) JYJProgramModel *programModel;

@property (nonatomic, weak) id<JYJMusicPlayerToolsDelegate>delegate;
//单例方法
+ (instancetype)shareMusicPlayerTools;
// 播放音乐
- (void)playMusic;
//暂停音乐
- (void)pauseMusic;
// 准备播放
- (void)preparePlayMusic;

// 跳转
- (void)seekToTimeWithValue:(CGFloat)value;


复制代码

2..m中

@property (nonatomic, strong) NSTimer *timer;// 滑动条定时器l;
@end
@implementation JYJMusicPlayerTools
//单例
+ (instancetype)shareMusicPlayerTools {
    static JYJMusicPlayerTools *mpTools = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mpTools = [[JYJMusicPlayerTools alloc]init];
    });
    return mpTools;
}
- (instancetype)init {
    self = [super init];
    if (self) {
        self.player = [[AVPlayer alloc]init];
        //为播放完毕添加观察者   播放完毕的操作  有外边决定
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endPlay:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    }
    return self;
}
// 播放完毕
- (void)endPlay:(NSNotification *)nati {
    //播放完毕先让音乐停止
    [self pauseMusic];
    if ([self.delegate respondsToSelector:@selector(endPlayAction)]) {
        [self.delegate endPlayAction];
    }
}
// 准备播放
- (void)preparePlayMusic {
    // 只要AVPlayer有currentTime,那么一定有观察者
    // 所以直接移除
    if (self.player.currentItem) {
        [self.player.currentItem removeObserver:self forKeyPath:@"status"];
    }
        
    
    self.playerItem= [[AVPlayerItem alloc]initWithURL:[NSURL URLWithString:self.programModel.music_media]];
    
    //为item的status添加观察者
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld  context:nil];
    //用新建的item,替换AVPlayer之前的item,新的item添加了观察者
    [self.player replaceCurrentItemWithPlayerItem:self.playerItem];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"status"]) {
        switch ([[change valueForKey:@"new"] integerValue]) {
            case AVPlayerItemStatusUnknown:
                NSLog(@"不知道什么错误");
                break;
            case AVPlayerItemStatusReadyToPlay:
                // 只有观察到status变为这种状态,才会真正的播放.
                [self playMusic];
                break;
            case AVPlayerItemStatusFailed:
                // mini设备不插耳机或者某些耳机会导致准备失败.
                NSLog(@"准备失败");
                if ([self.delegate respondsToSelector:@selector(playFail)]) {
                    [self.delegate playFail];
                    
                }
                break;
            default:
                break;
        }
    }
    
    

}
// 播放
- (void)playMusic {
    //如果定时器已经存在,说明已经在播放中,直接返回
    // 对于已经存在的定时器,只有pauseMusic方法才会使之停止和销毁
    if (self.timer != nil) {
        return;
    }
    // 播放后开启定时器
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    [self.player play];
}
- (void)timerAction:(NSTimer *)timer {
    //!!!!!!!!计时器的处理方法中,不断的调用代理方法,将音乐播放器的进度返回出去
    [self.delegate getCurTime:[self valueToString:[self getCurrentTime]] totle:[self valueToString:[self getTotalTime]] progress:[self getProgress]];
}
// 获取当前时间 (!!!!!就是这个算法 别问我为什么   规定的)
- (NSInteger)getCurrentTime {
    if (self.player.currentItem) {
        // 当前的时间value / 当前时间的时间比例
        return self.player.currentTime.value / self.player.currentTime.timescale;
    }
    return 0;
}
- (NSInteger)getTotalTime {
    CMTime totalTime = [self.player.currentItem duration];
    if (totalTime.timescale == 0) {
        return 1;
    }else {
        return totalTime.value / totalTime.timescale;
    }
}

// 获取当前播放进度
- (CGFloat)getProgress {
    return (CGFloat) [self getCurrentTime] / (CGFloat)[self getTotalTime];
}
// 将整数转化为00:00格式
- (NSString *)valueToString:(NSInteger)value {
    return [NSString stringWithFormat:@"%.2ld:%.2ld",value / 60,value % 60];
}


- (void)pauseMusic {
    [self.timer invalidate];
    self.timer = nil;
    [self.player pause];
}
// 跳转方法
- (void)seekToTimeWithValue:(CGFloat)value {
    // 跳转之前先停止
    [self.player pause];
    //跳转
    [self.player seekToTime:CMTimeMake(value * [self getTotalTime], 1) completionHandler:^(BOOL finished) {
        if (finished == YES) {
            [self.player play];
        }
    }];
}

复制代码

二. 我们将我们需要的音乐播放器类已经建完 我们讲一下数据解析类吧!(JYJDataTools) 1.h 中

@class JYJProgramModel;
typedef void(^PassValue)(NSArray *array);

@interface JYJDataTools : NSObject
@property (nonatomic, strong) NSMutableArray *dataArray;// 接受数据的数组
// 单例
+ (instancetype)shareDataTools;
- (void)postDataWithURL:(NSString *)URL andBody:(id)body passValue:(PassValue)passvalue;



- (JYJProgramModel *)getModelWithIndex:(NSInteger)index;// 第几个歌曲
@property (nonatomic, strong) JYJProgramModel *programModel;


复制代码

2.m中

// 单例创建
+ (instancetype)shareDataTools {
    static JYJDataTools *dataTools = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dataTools = [[JYJDataTools alloc]init];
    });
    return dataTools;
}
// 因为我的数据是post请求的 网址的url和body 体是不一样的而我的数据头是一样的,所以我只需要将网址的url和body和需要的数据封装在一起。
- (void)postDataWithURL:(NSString *)URL andBody:(id)body passValue:(PassValue)passvalue {
    [JYJNetWorkParser POST:URL andBody:body andBodyStyle:JYJBodyString andHeader:HEADDIC andResponse:JYJJSON andSuccessBlock:^(id result) {
        self.dataArray = [NSMutableArray array];
        NSArray *dataArray = [result objectForKey:@"data"];
        for (NSDictionary *dic in dataArray) {
            JYJProgramModel*model = [JYJProgramModel modelWithDic:dic];
            [self.dataArray addObject:model];
        }
        // !!!!!block回调
        passvalue(self.dataArray);
        
    } andFailureBlock:^(NSError *error) {
        
    }];
    

}
- (JYJProgramModel *)getModelWithIndex:(NSInteger)index {
    return self.dataArray[index];
}

复制代码

三.大体需要的类已经准备完毕,第三个类就需要自己来完成了,也就是铺带有暂停、播放、上一曲、下一曲、进度条的页面啦。

四.这时候我就当你已经铺好页面了。我们还的需要一个最重要的类了,也就是音乐播放控制器。当然前面也只是做一下铺垫,没有前面的他们我们什么也完不成!! 我们需要从有TableView的VC跳转到这个播放VC(JYJMusicController)

1.TableViewVC .m 中

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationController.navigationBar.translucent = NO;
    [self createView];
   [self handleData];
 
    // Do any additional setup after loading the view.
}

- (void)handleData {
  NSString *string = @"json=%7B%22session%22%3A%7B%22uid%22%3A%2233368%22%2C%22sid%22%3A%22d0f457f15aab43579fc6c37a4dce426c%22%7D%2C%22item_id%22%3A%2213241%22%2C%22keyword%22%3A%22%22%2C%22orderBy%22%3A2%2C%22recomm%22%3A0%2C%22pagination%22%3A%7B%22count%22%3A25%2C%22page%22%3A1%7D%7D";
        [[JYJDataTools shareDataTools]postDataWithURL:@"http://www.aiyinsitanfm.com/mobile/channel/musiclist.jhtml" andBody:string passValue:^(NSArray __kindof *array) {
            
            self.marrOfModel = array;
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.tableView reloadData];
               
            });
       }];
}
- (void)createView {
    self.tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
    [self.view addSubview:_tableView];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.tableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:@"pool"];
    
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
   
    return self.modelOfArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"pool" forIndexPath:indexPath];
    ModelOfFirst *model = self.modelOfArray[indexPath.row];
    [cell setModel:model];
    return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
   JYJMusicController*vc = [JYJMusicController shareVC];
    vc.index = indexPath.row;
    vc.model = self.modelOfArray[indexPath.row];
    [self.navigationController pushViewController:vc animated:YES];
}
复制代码

2.JYJMusicController .h中

@property (nonatomic, assign) NSInteger index;
@property (nonatomic, strong) ModelOfFirst *model;
+ (instancetype)shareVC;
复制代码

.m中

@interface ViewController ()<MusicPlayToolsDelegate>


@property (nonatomic, strong) MusicPlayView *viewContairner;
@property (nonatomic, strong) UIImageView *backImageView;

@property (nonatomic, strong) MusicPlayTools *aa;
@property (nonatomic, strong) UILabel *labelOfPaoma;
@property (nonatomic, strong) LSPaoMaView *paoma;
@end

@implementation ViewController
+ (instancetype)shareVC {
    static ViewController *vc = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        vc = [[ViewController alloc]init];
    });
    return vc;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.aa = [MusicPlayTools shareMusicPlayTools];
    self.aa.delegate = self;
    self.viewContairner = [[MusicPlayView alloc]initWithFrame:self.view.bounds];
    [self.view addSubview:self.viewContairner];
    [self.viewContairner play:^(MyButton *play) {
       [[MusicPlayTools shareMusicPlayTools] musicPause];
    } pause:^(MyButton *play) {
        [[MusicPlayTools shareMusicPlayTools]musicPlay];

    } lastSong:^(MyButton *play) {
        if (self.index > 0) {
            self.index --;
            
        }else{
            self.index = [GetDataTools shareGetData].dataArray.count - 1;
            
            
        }
        [self p_play];
    } nextSong:^(MyButton *play) {
        if (self.index == [GetDataTools shareGetData].dataArray.count -1) {
            self.index = 0;
            
        }else
        {
            self.index ++;
        
            
        }
        [self p_play];
    }];
   [self.viewContairner slider:^(UISlider *slider) {
       [[MusicPlayTools shareMusicPlayTools] seekToTimeWithValue:slider.value];
   } ];
    
    // Do any additional setup after loading the view, typically from a nib.
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.backImageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 300)];
    [self.view addSubview:_backImageView];
    [_backImageView sd_setImageWithURL:[NSURL URLWithString:self.model.music_img] placeholderImage:[UIImage imageNamed:@"zhanwei"]];

    [self p_play];
}
- (void)p_play {
    // 判断当前播放器的model 和 点击cell的index对应的model,是不是同一个.
    // 如果是同一个,说明正在播放的和我们点击的是同一个, 这个时候不需要重新播放.直接返回就行了.
    if ([[MusicPlayTools shareMusicPlayTools].model isEqual:[[GetDataTools shareGetData] getModelWithIndex:self.index]]) {
        return;
    }
    
    // 如果播放中和我们点击的不是同一个,那么替换当前播放器的model.
    // 然后重新准备播放.
    [MusicPlayTools shareMusicPlayTools].model = [[GetDataTools shareGetData] getModelWithIndex:self.index];
    //背景图片
    [self.backImageView sd_setImageWithURL:[NSURL URLWithString:[MusicPlayTools shareMusicPlayTools].model.music_img] placeholderImage:[UIImage imageNamed:@"zhanwei"]];
    //self.labelOfPaoma.text = [MusicPlayTools shareMusicPlayTools].model.title;
     self.paoma = [LSPaoMaView shareLsinitWithFrame:CGRectZero title:[MusicPlayTools shareMusicPlayTools].model.music_title BackColor:[UIColor redColor] textColor:[UIColor whiteColor] textFont:25];
    NSLog(@"%@",[MusicPlayTools shareMusicPlayTools].model.music_title);
    // 注意这里准备播放 不是播放!!!
    [[MusicPlayTools shareMusicPlayTools] musicPrePlay];
}


-(void)getCurTime:(NSString *)curTime Totle:(NSString *)totle Progress:(CGFloat)progress {
    self.viewContairner.leftLabelTime.text = curTime;
    self.viewContairner.rightLabelTime.text = totle;
    self.viewContairner.slider.value = progress;
}
- (void)endOfPlayAction {
    if (self.index == [GetDataTools shareGetData].dataArray.count -1) {
        self.index = 0;
    }else
    {
        self.index ++;
    }
    [self p_play];
}
- (void)playFail {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"节目加载失败" preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"确认" style: UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
    }];
    [alert addAction:action];
    [self presentViewController:alert animated:YES completion:nil];
    
}

复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值