IOS 7 SDK не поддерживает фоновый звук
Я провел много исследований, как в Google, так и в StackOverflow. Все ответы, которые я нашел, не работают в iOS 7. Я начал писать новое приложение в iOS 7 SDK с Xcode 5.
Все, что я пытаюсь сделать, это воспроизвести аудио в приложении из файла, хранящегося в комплекте приложения (не из библиотеки музыки). Я хочу, чтобы звук воспроизводился в фоновом режиме и контролировался при блокировке экрана (в дополнение к Control Center).
Я установил ключ APPNAME-Info.plist
, UIBackgroundModes
, в аудио. Он не обрабатывает вещи в делегате приложения; все делается внутри ViewController
@interface ViewController : UIViewController <AVAudioPlayerDelegate>
В рамках реализации метода viewDidAppear:
я вызываю super, а затем следующий код:
// Once the view has loaded then we can register to begin receiving controls and we can become the first responder
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
В моем методе реализации viewWillDisappear:
у меня есть следующий код:
// End receiving events
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
Я также внедрил метод canBecomeFirstResponder
, который возвращает ДА. Затем я применил метод remoteControlReceivedWithEvent:
:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
// If it is a remote control event handle it correctly
if (event.type == UIEventTypeRemoteControl) {
if (event.subtype == UIEventSubtypeRemoteControlPlay) {
[self playPauseAudio:self];
} else if (event.subtype == UIEventSubtypeRemoteControlPause) {
[self playPauseAudio:self];
} else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {
[self playPauseAudio:self];
}
}
}
Что меня смущает, так это то, что эта точная настройка отлично работает на iOS 6. На iOS 7 она не работает. Раньше это было так просто в iOS 6. Что-то принципиально изменилось в iOS 7 SDK. Что мне не хватает?
Ответы
Ответ 1
Мне удалось это решить, и сохранить волосы, тянущие еще одна бедная душа, здесь:
Во-первых, убедитесь, что ваш Info.plist правильно отображает аудио в качестве фонового режима.
(Если вы не знаете, что я говорю, выберите YOURAPPNAME-Info.plist, выберите это. Нажмите знак плюса и добавьте новый ключ с именем UIBackgroundModes
и разверните его. Добавьте значение под названием audio
.)
Вам понадобится ссылка на любой объект воспроизведения, создающий аудио. Поскольку я только воспроизвожу аудио и AVplayer не соблюдает фоновый звук, используйте это в своем заголовке контроллера вида:
@property (nonatomic, retain) MPMoviePlayerController *audioPlayer;
В реализации выполните следующие действия:
[super viewDidAppear:animated];
//Once the view has loaded then we can register to begin recieving controls and we can become the first responder
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
и
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
//End recieving events
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
добавить два метода
//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void) registerForAudioObjectNotifications {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver: self
selector: @selector (handlePlaybackStateChanged:)
name: MixerHostAudioObjectPlaybackStateDidChangeNotification
object: audioObject];
}
теперь ВСЕ важный код - это позволяет вашему приложению управлять аудио из "центра управления" и с экрана блокировки:
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[self playOrStop: nil];
break;
default:
break;
}
}
}
здесь вы можете добавить много типов типов событий и вызвать любой метод.
Типичные события:
UIEventSubtypeRemoteControlPlay = 100, //Parent EVENT
// All below are sub events and you can catch them using switch or If /else.
UIEventSubtypeRemoteControlPause = 101,
UIEventSubtypeRemoteControlStop = 102,
UIEventSubtypeRemoteControlTogglePlayPause = 103,
UIEventSubtypeRemoteControlNextTrack = 104,
UIEventSubtypeRemoteControlPreviousTrack = 105,
UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
UIEventSubtypeRemoteControlEndSeekingBackward = 107,
UIEventSubtypeRemoteControlBeginSeekingForward = 108,
UIEventSubtypeRemoteControlEndSeekingForward = 109,
Чтобы отладить справку, вы можете использовать:
MPMoviePlayerController *mp1= (MPMoviePlayerController *)[notification object];
NSLog(@"Movie State is: %d",[mp1 playbackState]);
switch ([mp1 playbackState]) {
case 0:
NSLog(@"******* video has stopped");
break;
case 1:
NSLog(@"******* video is playing after being paused or moved.");
break;
case 2:
NSLog(@"******* video is paused");
break;
case 3:
NSLog(@"******* video was interrupted");
break;
case 4:
NSLog(@"******* video is seeking forward");
break;
case 5:
NSLog(@"******* video is seeking Backwards");
break;
default:
break;
и это все - надеюсь, что это поможет кому-то! - это отлично работает на iOS 7 и iOS 6 с помощью приложения Storyboard, а также управляет с помощью наушников и всего нового центра управления.
Ответ 2
Очевидно, проблема была на стороне Apple, поскольку обновление iOS 7.0.3 устраняет эту проблему. Кроме того, что Алекс заметил о изменениях UIEventSubtype, код, который работал на iOS6, теперь работает на iOS7.
Для полноты, вот мой соответствующий код, который работает как в iOS6, так и в iOS7 - после udpate до 7.0.3. Также включены AVFoundation.framework и MediaPlayer.framework в Project Build Phases → Link binary с библиотеками. Нет кода для этого в делегате приложения.
В файле viewcontroller.h:
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
@interface NewsDetailViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>
@property (nonatomic) MPMoviePlayerController *audioPlayer;
В файле viewcontroller.m:
- (void)viewDidLoad
{
[super viewDidLoad];
self.audioPlayer = [[MPMoviePlayerController alloc] initWithContentURL:audioUrl];
[self.audioPlayer prepareToPlay];
[self.audioPlayer.view setFrame:CGRectMake(0, 0, self.audioView.frame.size.width, 42)];
self.audioPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.audioView addSubview:self.audioPlayer.view];
[self.audioPlayer play];
NSError *setCategoryError = nil;
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&activationError];
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self.audioPlayer stop];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
[self.audioPlayer play];
break;
case UIEventSubtypeRemoteControlPause:
[self.audioPlayer pause];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
if (self.audioPlayer.playbackState == MPMoviePlaybackStatePlaying) {
[self.audioPlayer pause];
}
else {
[self.audioPlayer play];
}
break;
default:
break;
}
}
}
Ответ 3
Если вы хотите воспроизводить аудио в фоновом режиме в iphone и симуляторе, тогда вам нужно написать этот код в plist и во-первых, убедитесь, что ваш Info.plist правильно отображает аудио в качестве фонового режима.
(Если вы не знаете, что я говорю о выборе YOURAPPNAME-Info.plist, выберите это. Нажмите на знак плюса и введите ключ UIBackgroundModes и введите. Добавьте значение, называемое "Воспроизведение приложений" (для симулятора) или "Приложение воспроизводит аудио или передает аудио/видео с помощью AirPlay" (для iphone).)
![enter image description here]()
в AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier task = 0;
task=[application beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"Expiration handler called %f",[application backgroundTimeRemaining]);
[application endBackgroundTask:task];
task=UIBackgroundTaskInvalid;
}];
}
Добавьте эти две структуры в свой проект и некоторую строку кода в ViewController.h
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
@interface ViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>
@property (nonatomic) MPMoviePlayerController *audioPlayer;
Напоминаем, что эти фреймворки должны быть добавлены в ваш проект.
Затем в Viewcontrller.m
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self.audioPlayer stop];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL *audioUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"songName" ofType:@"mp3"]];
self.audioPlayer = [[MPMoviePlayerController alloc] initWithContentURL:audioUrl];
[self.audioPlayer prepareToPlay];
[self.audioPlayer.view setFrame:CGRectMake(0, 0, self.view.frame.size.width-100, 42)];
self.audioPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.audioPlayer.view];
[self.audioPlayer play];
// for run application in background
NSError *setCategoryError = nil;
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&activationError];
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
}
Я надеюсь, что это поможет вам воспроизвести аудио в фоновом режиме в iphone и симуляторе.
Ответ 4
Следует отметить, что отличие от iOS6 до iOS7 с событиями удаленного управления заключается в том, что в iOS6 события play/pause появились как один UIEventSubtype
:
UIEventSubtypeRemoteControlTogglePlayPause
И в iOS7 они приходят как два отдельных подтипа:
UIEventSubtypeRemoteControlPause
UIEventSubtypeRemoteControlPlay
Ответ 5
Так как я также заинтересован в поиске этого решения, я добавлю еще немного информации с моей стороны.
Я испытываю ту же проблему, но документация Apple не изменилась в отношении управления событиями дистанционного управления.
Я попытался переместить кое-что, и произошло что-то интересное.
Я первоначально имел управление событиями дистанционного управления в моем контроллере TabBar. Теперь, когда я переместил все в контроллере просмотра плеера, я вижу, что я могу снова управлять воспроизведением музыки с помощью своей гарнитуры, но не с кнопок на новой панели управления iOS7 (той, которая появляется снизу на экране).
Это довольно странно.
Чтобы решить проблему, попробуйте пополнить поток нашим тестом.