24

我在 Google 和 StackOverflow 上做了很多研究。我找到的所有答案都不适用于 iOS 7。我开始使用 Xcode 5 在 iOS 7 SDK 中编写新的应用程序。

我要做的就是从存储在应用程序包中的文件(而不是音乐库)中播放应用程序中的音频。我想在屏幕锁定时在后台播放音频并进行控制(除了控制中心)。

我将APPNAME-Info.plist键 ,设置UIBackgroundModesaudio。它不处理应用程序委托中的事情;一切都在 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返回YES的方法。接下来,我实现了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 中发生了根本性的变化。我错过了什么?

4

5 回答 5

16

我设法解决了这个问题,并且为了避免被另一个可怜的灵魂拉扯头发:

首先确保您的 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 应用程序以及使用耳机和所有新控制中心的控制非常完美。

于 2013-09-26T23:28:43.333 回答
7

显然问题出在 Apple 一方,因为 iOS 更新 7.0.3 解决了这个问题。除了 Alex 提到的 UIEventSubtype 更改之外,在 iOS6 上运行的代码现在在 iOS7 上运行。

为了完整起见,这是我在 iOS6 和 iOS7 中工作的相关代码 - 在更新到 7.0.3 之后。项目 Build Phases -> Link binary with libraries 中还包括 AVFoundation.framework 和 MediaPlayer.framework。应用委托中没有此代码。

在视图控制器 .h 文件中:

#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>

@interface NewsDetailViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>
@property (nonatomic) MPMoviePlayerController *audioPlayer;

在视图控制器 .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;
        }
    }
}
于 2013-10-23T14:23:07.613 回答
6

如果您还想在 iphone 和模拟器的后台播放音频,那么您需要在 plist 中编写此代码,首先确保您的 Info.plist 正确地将音频列为背景模式。

(如果您不知道我在说什么,请选择 YOURAPPNAME-Info.plist 选择它。单击加号并键入一个键 UIBackgroundModes 并输入。添加一个名为“应用程序播放音频”(用于模拟器)或“应用程序播放”的值使用 AirPlay 播放音频或流式传输音频/视频”(适用于 iphone)。)

在此处输入图像描述

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;

提醒您应该在您的项目中添加这些框架引用。

然后在Viewcontroller.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 和模拟器的背景中播放音频。

于 2014-01-04T10:31:49.507 回答
4

需要注意的一点是,从 iOS6 到 iOS7 的远程控制事件不同的是,在 iOS6 中,播放/暂停事件是一个UIEventSubtype
UIEventSubtypeRemoteControlTogglePlayPause
而在 iOS7 中,它们作为两个独立的子类型出现:
UIEventSubtypeRemoteControlPause
UIEventSubtypeRemoteControlPlay

于 2013-10-04T06:09:45.287 回答
0

因为我也有兴趣找到这个解决方案,所以我会从我这边添加一些更多信息。

我遇到了同样的问题,但是关于远程控制事件管理的 Apple 文档仍然没有改变。

我试着移动一些东西,发生了一些有趣的事情。

我最初在 TabBar 控制器中有远程控制事件管理。现在我移动了播放器视图控制器中的所有内容,我可以看到我可以再次使用耳机控制音乐播放,但不能通过 iOS7 新控制面板中的按钮(来自屏幕底部的按钮)。

这很奇怪。

为了解决这个问题,让我们尝试用我们的测试来丰富线程。

于 2013-09-26T09:22:37.377 回答