前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS_AVAudioPlayer播放并实现了后台播放和远程控制

iOS_AVAudioPlayer播放并实现了后台播放和远程控制

作者头像
mikimo
发布2022-07-20 13:30:58
8180
发布2022-07-20 13:30:58
举报
文章被收录于专栏:iOS开发~iOS开发~
代码语言:javascript
复制
//  ViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h> 

@class AVAudioPlayer;

@interface PlayViewController : UIViewController<AVAudioPlayerDelegate>

@property(nonatomic, strong) AVAudioPlayer *player;

+ (PlayViewController *)defaultPlayVC; //将该播放页面定义成单例

@end

代码语言:javascript
复制
//  ViewController.m
#import "PlayViewController.h"

@interface PlayViewController ()

@property (retain, nonatomic) IBOutlet UIButton *suspendBtn;  //暂停按钮
@property (retain, nonatomic) IBOutlet UIButton *formerBtn;   //上一首按钮
@property (retain, nonatomic) IBOutlet UIButton *nextBtn; //下一首按钮
@property (retain, nonatomic) IBOutlet UISlider *slider;  //滑块
@property (retain, nonatomic) NSArray *musicUrlArray; //音乐网址数组
@property (retain, nonatomic) NSArray *musicAllTimeArray; //音乐时间数组
@property (retain, nonatomic) NSTimer *timer;  //计时器
@property (assign, nonatomic) int currentIndex; //当前音频下标

@end

@implementation PlayViewController
代码语言:javascript
复制
#pragma mark 单例初始化方法
+ (PlayViewController *)defaultPlayVC {
    static PlayViewController *playVC = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        playVC = [[PlayViewController alloc] init];
    });
    return playVC;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.currentIndex = 0;
    self.musicUrlArray = [NSArray arrayWithObjects:@"http://fdfs.xmcdn.com/group10/M08/D4/2E/wKgDaVZ9EYaQ-j48AFIOaAXL-wM019.mp3",
                          @"http://fdfs.xmcdn.com/group13/M09/D4/17/wKgDXVZ6fR2wR1E8AFuO_VClRZA066.mp3",
                          @"http://fdfs.xmcdn.com/group16/M0A/D5/16/wKgDbFZ89SqyFGWGAHoEKd7yPsw709.mp3",
                          @"http://fdfs.xmcdn.com/group8/M01/D4/34/wKgDYFZ6y1nR6QpZAEDVy7rUTTc247.mp3",
                          @"http://fdfs.xmcdn.com/group9/M00/D4/EF/wKgDZlZ7aLGCQEEDAHMsQsurq_Y170.mp3",
                          @"http://fdfs.xmcdn.com/group12/M07/D4/C4/wKgDW1Z70rWxoDA7ABAp1lwS5nI990.mp3", nil];
    self.musicAllTimeArray = [NSArray arrayWithObjects:@(1344.36), @(1500.04), @(1999.06), @(1062.22), @(1886.96), @(264.76), nil];
    
    //滑块
    self.slider.minimumValue = 0;
    [self.slider addTarget:self action:@selector(changValue:) forControlEvents:UIControlEventValueChanged];  //添加滑动事件
    [self.slider setThumbImage:[UIImage imageNamed:@"playProcessDot_n"]forState:UIControlStateNormal];

    //播放音频之前先要设置AVAudioSession模式 是它可以后台播放
    //并且要在plist文件中 添加required background modes这个key项,并选择"App plays audio or streams audio/video using AirPlay"这个value项。
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
    
    [self playerCurrentMusic];
}

-(void)playerCurrentMusic {

    //创建串行队列 (放入线程中,免得页面假死)
    dispatch_queue_t serialQueue = dispatch_queue_create("com.audio.www", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{

        NSString *url = self.musicUrlArray[self.currentIndex];
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
        self.player = [[AVAudioPlayer alloc] initWithData:data error:nil];
        self.player.delegate = self;
        [self.player prepareToPlay];
        [self.player play];

        //滑块
        CGFloat allTime = [self.musicAllTimeArray[self.currentIndex] floatValue];
        self.slider.maximumValue = allTime;
        
        //计时器只能在主线程中
        dispatch_async(dispatch_get_main_queue(), ^{
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(reloadAction) userInfo:nil repeats:YES];
        });
    });
}

#pragma mark -- 每1秒 给进度条赋一次值
-(void)reloadAction {
    if ([self.player isPlaying]) {
        self.slider.value = self.player.currentTime;
    }
}

#pragma mark 点击暂停
- (IBAction)suspendAction:(UIButton *)sender {
    sender.selected = !sender.selected;
    if (sender.selected) {
        [self.player pause]; //暂停
        [sender setTitle:@"播放" forState:UIControlStateSelected];
    } else {
        [self.player play];  //播放
        [sender setTitle:@"暂停" forState:UIControlStateNormal];
    }
}

#pragma mark 点击上一首
- (IBAction)formerAction:(UIButton *)sender {
    [self myPlayFormer]; //播放上一首
}

#pragma mark 点击下一首
- (IBAction)nextAction:(UIButton *)sender {
    [self myPlayNext];  //播放下一首
}

#pragma mark 上一首
-(void)myPlayFormer {
    if (self.currentIndex > 0) {
        [self.player pause];
        self.currentIndex--;
        [self playerCurrentMusic];
    }
}
#pragma mark 下一首
-(void)myPlayNext {
    if (self.currentIndex < self.musicUrlArray.count - 1) {
        [self.player pause];
        self.currentIndex++;
        [self playerCurrentMusic];
    }
}

/*****************   控制台   *****************/
#pragma mark -- 声明App接收远程控制事件
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

#pragma mark -- App结束声明接收远程控制事件
- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
}

#pragma mark -- 接受控制台的控制事件
- (void)remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
    if (receivedEvent.type == UIEventTypeRemoteControl) {
        //判断点击按钮的类型
        switch (receivedEvent.subtype) {
                
            case UIEventSubtypeRemoteControlPlay:
                [self.player play];  //播放
                break;
            case UIEventSubtypeRemoteControlPause:
                [self.player pause]; //暂停
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                [self myPlayFormer]; //播放上一首
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                [self myPlayNext];  //播放下一首
                break;
            default:
                break;
        }
    }
}

#pragma mark 实现后台播放完一首后, 继续播放下一首
- (BOOL)canBecomeFirstResponder {
    return YES;
}

/*****************   AVAudioPlayer 代理   *****************/
#pragma mark -- 后台播放被打断, 继续恢复播放 (比如打电话...)
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags {
    [self.player play];
}
#pragma mark -- 播放完当前声音后,播放下一首
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (self.currentIndex < self.musicUrlArray.count - 1) {
        self.currentIndex++;
        [self playerCurrentMusic];
    }
}

#pragma mark -- 滑块滑动改变播放进度(代理方法)
-(void)changValue:(UISlider *)slider {
    //拖动滑块时, 停止计时器
    [self.timer invalidate];
    self.player.currentTime = slider.value;
    //开始计时器
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(reloadAction) userInfo:nil repeats:YES];
}

想要实现后台播放(即程序进入后台,音乐继续播放)需要修改plist文件:

代码语言:javascript
复制
1.添加required background modes这个key项,并选择"App plays audio or streams audio/video using AirPlay"这个value项。

2.XCode7.1以上获取网络数据,需要修改plist文件

在Info.plist中添加App Transport Security Settings类型Dictionary。

在App Transport Security Settings下添加Allow Arbitrary Loads类型Boolean,值设为YES

需要在后台实现播放页的各种上一首,下一首...各种功能, 还需要在Appdelegate.m的background方法里:

代码语言:javascript
复制
- (void)applicationDidEnterBackground:(UIApplication *)application {
    
    //程序进入后台后, 为了让音乐继续播放, 在此方法里创建该代理播放页, 并指定它播放器的代理
    PlayViewController *player = [PlayViewController defaultPlayVC];
    player.player.delegate = player;
}

当前工程Demo地址: http://download.csdn.net/detail/margaret_mo/9412069

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档