I successfully enabled my app to be able to play audio and video in background after the screen is locked. However, for better user experience I want to show play and pause controls of the running media on the locked screen. After following couple of blogs online, added the following code:
#interface MyControllerClass () <UIGestureRecognizerDelegate, UIApplicationDelegate>
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:avAsset];
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
}
- (void)viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (BOOL) canBecomeFirstResponder {
return YES;
}
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
NSLog(#"received event %#",receivedEvent);
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause: {
if ([self isVideoPlaying]) {
[self.avPlayer pause];
} else {
[self.avPlayer play];
}
break;
}
case UIEventSubtypeRemoteControlPlay: {
[self.avPlayer play];
break;
}
case UIEventSubtypeRemoteControlPause: {
[self.avPlayer pause];
break;
}
default:
break;
}
}
}
Added background modes in info.plist
Even though I am able to see the control screen, no user event is received by my app upon clicking the buttons.
I believe I am missing out on something very obvious. Any pointers would be helpful.
EDIT 1: The accepted answer in iOS - UIEventTypeRemoteControl events not received says that Your app must be the “Now Playing” app. How do I do this?
I found the answer to my question. I need to implement the code in my question in AppDelegate to receive events instead of implementing in ViewController.
Related
If the app is playing the audio and phone screen is locked then control screen is shown as below. I am not able to take any action on avplayer
In my appdelegate I implemented:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter];
[[rcc skipForwardCommand] setEnabled:NO];
[[rcc skipBackwardCommand] setEnabled:NO];
[[rcc nextTrackCommand] setEnabled:NO];
[[rcc previousTrackCommand] setEnabled:NO];
[[rcc skipForwardCommand] setEnabled:NO];
[[rcc skipBackwardCommand] setEnabled:NO];
rcc.playCommand.enabled = YES;
rcc.pauseCommand.enabled = YES;
[[MPRemoteCommandCenter sharedCommandCenter].playCommand addTarget:self action:#selector(play)];
[[MPRemoteCommandCenter sharedCommandCenter].pauseCommand addTarget:self action:#selector(pause)];
}
- (void) play {
[[MyVideoController instance] play];
}
- (void) pause {
[[MyVideoController instance] pause];
}
class MyVideoController consists of:
- (void) pause {
[self.avPlayer pause];
}
- (void) play {
[self.avPlayer play];
}
Even though these methods are triggered (added breakpoints to check), no action on avplayer is taken. No matter what, avplayer doesn't pause.
Is there any way to pause the avplayer?
EDIT 1:
Adding the complete code
In my AppDelegate:
- (void) remoteControlReceivedWithEvent: (UIEvent *) event {
[[ZVideoPlayerController instance] eventReceived:event];
if (event.type == UIEventTypeRemoteControl) {
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause: {
break;
}
case UIEventSubtypeRemoteControlPlay: {
[[ZVideoPlayerController instance] play];
break;
}
case UIEventSubtypeRemoteControlPause: {
[[ZVideoPlayerController instance] pause];
break;
}
default:
break;
}
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}
I AM RECEIVING EVENTS BUT THE AUDIO DOESN'T PAUSE UPON CALLING PAUSE METHOD ON AVPLAYER.
EDIT 2:
instance declaration in PlayerController class
+ (instancetype)instance {
static id instance = nil;
if (instance == nil)
{
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^(void) {
NSAssert(instance == nil, #"Singleton instance is already allocated.");
instance = [[super allocWithZone:NULL] init];
});
}
return instance;
}
initialising AVPlayer
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:avAsset];
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: [UIImage imageNamed:#"Audio_Thumbnail_Play"]];
[songInfo setObject:title forKey:MPMediaItemPropertyTitle];
[songInfo setObject:#"100" forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
self.avPlayer = [AVPlayer playerWithPlayerItem:playerItem];
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
I found a solution to the problem. As I was getting nil value of avPlayer, I used my PageViewController class to get the instance of PlayerController. Then I used the instance of this playerController to play and pause my avplayer because this instance holds the reference to avPlayer.
- (PlayerController *)getVideoController {
NSArray *controllers = [UtiliyClass getNavigationController].viewControllers;
PageViewController *pageController = nil;
for (UIViewController *cont in controllers) {
if ([cont isKindOfClass:[PageViewController class]]) {
pageController = (PageViewController *)cont;
break;
}
}
if (pageController == nil) {
return nil;
}
NSArray *objectsController =pageController.pageController.viewControllers;
PlayerController *videoPlayerController = nil;
for (UIViewController *item in objectsController) {
if ([item isKindOfClass:[PlayerController class]]) {
videoPlayerController = (PlayerController *)item;
break;
}
}
return videoPlayerController;
}
- (void) pause {
PlayerController *controller = [self getVideoController];
[controller.avPlayer pause];
}
- (void) play {
PlayerController *controller = [self getVideoController];
[controller.avPlayer play];
}
You need to register for remote notification to update player state when application is locked.For that follow following:
Add this in your AppDelegate , Ideally in applicationDidEnterBackground:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
And this in applicationDidBecomeActive:
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
Recieve remote notifcations by adding this in AppDelagate. This will listen all actions when phone is locked.
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl){
// Call method of your player where you want to make change (Pause , Paly),
// I am calling a shared view for example, Its up to your logic how you want to deal it
[[AudioPlayerView sharedPlayerView] remoteControlReceivedWithEvent:event];
}
}
And in that get your desired event and update state accordingly
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl){
switch (event.subtype){
case UIEventSubtypeRemoteControlPlay:
[[MyVideoController instance] play];
break;
case UIEventSubtypeRemoteControlPause:
[[MyVideoController instance] pause];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
// Check if state is playing , call pause else call play
break;
}
default:
break;
}
}
}
In iOS 7.1 and later, use the shared MPRemoteCommandCenter object to register for remote control events. You do not need to call this method when using the shared command center object.
This method starts the delivery of remote control events using the responder chain. Remote-control events originate as commands issued by headsets and external accessories that are intended to control multimedia presented by an app. To stop the reception of remote-control events, you must call endReceivingRemoteControlEvents().
Add this following code for in didfinishlunching for init audio season and get remote control event :
// Initialize the AVAudioSession here.
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&myErr]) {
// Handle the error here.
NSLog(#"Audio Session error %#, %#", myErr, [myErr userInfo]);
}
else{
// Since there were no errors initializing the session, we'll allow begin receiving remote control events
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
for reciving commadn use this code :
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPreviousTrack:
break;
case UIEventSubtypeRemoteControlNextTrack:
break;
case UIEventSubtypeRemoteControlPlay:
[[MyVideoController instance] play];
break;
case UIEventSubtypeRemoteControlPause:
[[MyVideoController instance] pause];
break;
default:
break;
}
}
}
So I have an app that plays a bunch of songs while the user can flip through a comic book. I use AVAudioPlayer and I have it set up to play the songs in a set order. So when one song finishes, the next one will play. This works flawlessly when the app is open. The problem occurs when the app is in the background. I set up the app to play in the background, and that works fine. So when the user presses the home screen the music continues to play. The problem occurs when the song ends, it is suppose to play the next song like it does when the app is open. Instead nothing happens. According to the my NSLog statements the correct methods are being called but nothing happens. Here is my code:
- (void)audioPlayerDidFinishPlaying: (AVAudioPlayer *)player successfully: (BOOL) flag {
NSLog(#"Song finished");
if ([songSelect isEqualToString: #"01icecapades"]) {
isPlay = #"yes";
songSelect = #"02sugarcube";
imageSelect = #"playbanner02";
[self performSelector:#selector(triggerSong) withObject:nil afterDelay:0];
[self performSelector:#selector(triggerBanner) withObject:nil afterDelay:0];
}
else if ([songSelect isEqualToString: #"02sugarcube"]) {
isPlay = #"yes";
songSelect = #"03bullets";
imageSelect = #"playbanner03";
[self performSelector:#selector(triggerSong) withObject:nil afterDelay:0];
[self performSelector:#selector(triggerBanner) withObject:nil afterDelay:0];
}
else if ([songSelect isEqualToString: #"03bullets"]) {
isPlay = #"yes";
songSelect = #"04satanama";
imageSelect = #"playbanner04";
[self performSelector:#selector(triggerSong) withObject:nil afterDelay:0];
[self performSelector:#selector(triggerBanner) withObject:nil afterDelay:0];
}
else if ([songSelect isEqualToString: #"04satanama"]) {
isPlay = #"yes";
songSelect = #"05uglyjoke";
imageSelect = #"playbanner05";
[self performSelector:#selector(triggerSong) withObject:nil afterDelay:0];
[self performSelector:#selector(triggerBanner) withObject:nil afterDelay:0];
}
else if ([songSelect isEqualToString: #"05uglyjoke"]) {
isPlay = #"yes";
songSelect = #"01icecapades";
imageSelect = #"playbanner01";
[self performSelector:#selector(triggerSong) withObject:nil afterDelay:0];
[self performSelector:#selector(triggerBanner) withObject:nil afterDelay:0];
}}
Above is the code that recognizes which song is playing, and sets the correct song next. Then it triggers another method that sets up the player.
- (void)triggerSong {
NSLog(#"triggerSong called");
NSString *path;
NSError *error;
// Path the audio file
path = [[NSBundle mainBundle] pathForResource:songSelect ofType:#"mp3"];
// If we can access the file...
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
// Setup the player
player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
//player = [initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
[player setDelegate: self];
// Set the volume (range is 0 to 1)
player.volume = 1.0f;
[player prepareToPlay];
[player setNumberOfLoops:0];
[player play];
NSLog(#"player play");
[error release];
player.delegate = self;
// schedules an action every second for countdown
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateTimeLeft) userInfo:nil repeats:YES];
}}
Now I assuming this is not the best way to do this, but it works great when the app is in the foreground state. I've been looking through the documentation and I can't seem to find the cause of this problem. I was hoping somebody might be able to see an error to my approach. Like I said before, the two NSLogs in the triggerSong method are being called so I can't see why the AVAudioPlayer (player) is not being called.
Also I have the correct setting in my info.plist and I have this in my viewDidLoad:
//Make sure the system follows our playback status
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
Thanks for any insight. Much appreciated.
Relevant discussion
SHORT ANSWER:
You need this code in either your first view controller's init or viewDidLoad method:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
LONG ANSWER W/ SAMPLE:
Here is my example. Like you, I began with an app that would play music in the background but could never continue playing after the first clip ended. I made a copy of the original Music.mp3 and named it Music2.mp3. My intention was to play Music2.mp3 as soon as Music.mp3 ended (audioPlayerDidFinishPlaying:). I goofed around with the background tasks for awhile until I got this working WITHOUT the background task:
-(id)init{
self = [super initWithNibName:#"MediaPlayerViewController" bundle:nil];
if(self){
//Need this to play background playlist
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
//MUSIC CLIP
//Sets up the first song...
NSString *musicPath = [[NSBundle mainBundle] pathForResource:#"Music" ofType:#"mp3"];
if(musicPath){
NSURL *musicURL = [NSURL fileURLWithPath:musicPath];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil];
[audioPlayer setDelegate:self];
}
}
return self;
}
-(IBAction)playAudioFile:(id)sender{
if([audioPlayer isPlaying]){
//Stop playing audio and change test of button
[audioPlayer stop];
[sender setTitle:#"Play Audio File" forState:UIControlStateNormal];
}
else{
//Start playing audio and change text of button so
//user can tap to stop playback
[audioPlayer play];
[sender setTitle:#"Stop Audio File" forState:UIControlStateNormal];
}
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
[audioButton setTitle:#"Play Audio File" forState:UIControlStateNormal];
[playRecordingButton setTitle:#"Play Rec File" forState:UIControlStateNormal];
//PLAY THE SECOND SONG
NSString *musicPath2 = [[NSBundle mainBundle] pathForResource:#"Music2" ofType:#"mp3"];
if(musicPath2){
NSURL *musicURL2 = [NSURL fileURLWithPath:musicPath2];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL2 error:nil];
[audioPlayer setDelegate:self];
NSLog(#"Play it again: \n%#", musicPath2);
[audioPlayer play];
}
}
The end result is that my app is now playing Music2.mp3 on a continuous loop, even if the app is in the background.
Just to confirm what Squatch said, this is also the solution in Swift:
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
OS X exhibits the same problem using AVAudioPlayer, however UIApplication is an iOS-only construct. OS X requires using NSApplication instead, but NSApplication doesn't return until the application is terminating so we need to use threads. As a bonus, there's an assert() somewhere in the depths of NSApplication that demands the main thread.
This hybrid C++/Objective C function is one workaround for this OS X issue:
void do_the_dumb (void real_application(void)) {
std::thread thread ([real_application]() {
real_application();
[[NSApplication sharedApplication] terminate: [NSApplication sharedApplication]];
});
[[NSApplication sharedApplication] run];
thread.join();
};
I have an application that plays audio in the background. I am trying to fix a bug where the audio controls (play/pause), on the home screen (etc.), DO NOT work on iOS 8.0+ but work FINE on iOS 7.0. I have been digging through trying to figure out what the issue is and have been coming up empty. Any ideas would be greatly appreciated. Here is what I have in place.
In the Project Settings:
I have ensured that UIBackgroundModes is set to audio.
In the AppDelegate.h:
I have a member for the AVAudioSession* session; as well as the AVPlayer *audioPlayer;
In the AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
self.session = [AVAudioSession sharedInstance];
NSError* error = NULL;
[self.session setCategory:AVAudioSessionCategoryPlayback error:&error];
[self.session setActive:YES error:&error];
if (error) {
NSLog(#"AVAudioSession error: %#", [error localizedDescription]);
}
In the AudioPlayerViewController.m
- (void)viewDidLoad {
//Get the Audio
NSURL *url = [NSURL URLWithString:self.audioUrl];
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
//Setup the player
self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
appDelegate.audioPlayer = [AVPlayer playerWithPlayerItem:self.playerItem];
//Setup the "Now Playing"
NSMutableDictionary *mediaInfo = [[NSMutableDictionary alloc]init];
[mediaInfo setObject:self.title forKey:MPMediaItemPropertyTitle];
[mediaInfo setObject:self.artist forKey:MPMediaItemPropertyArtist];
[mediaInfo setObject:self.album forKey:MPMediaItemPropertyAlbumTitle];
[mediaInfo setObject:[NSNumber numberWithDouble:duration ] forKey:MPMediaItemPropertyPlaybackDuration];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:mediaInfo];
}
// Process remote control events
- (void) remoteControlReceivedWithEvent:(UIEvent *)event {
NSLog(#"AudioPlayerViewController ... remoteControlReceivedWithEvent top ....subtype: %d", event.subtype);
if (event.type == UIEventTypeRemoteControl) {
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[self togglePlayPause];
break;
case UIEventSubtypeRemoteControlPause:
[self doPause];
break;
case UIEventSubtypeRemoteControlStop:
[self doPause];
break;
case UIEventSubtypeRemoteControlPlay:
[self doPlay];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[self nextOrPrevTrack];
break;
case UIEventSubtypeRemoteControlNextTrack:
[self nextOrPrevTrack];
break;
default:
break;
}
}
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (BOOL) canBecomeFirstResponder {
return YES;
}
Finally figured out the issue I was having. Ultimately it seemed that the events from the Remote Control on the home screen were never making it into my app and down to my view controllers. I ended up subclassing the UIWindow so that I could see what events were making their way through the chain. As UIWindow is a UIResponder I also added the - (void)remoteControlReceivedWithEvent:(UIEvent *)event to the subclass. Then in the makeKeyAndVisible I added the:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
I started up the debugger and the -(void)makeKeyAndVisible was never called! I then searched my app delegate for the window member variable and the [window makeKeyAndVisible]; line was nowhere to be found! I added it back in (as it should have been there) and presto events are routing to the correct locations like magic. Why this was working on some versions of iOS and not others and without any other noticeable issues is beyond me.
Hope this helps someone out in the future.
In your ViewController add
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
self.becomeFirstResponder()
}
override func remoteControlReceivedWithEvent(event: UIEvent) {
// your stuff
}
In AppDelegate add
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var error: NSError?
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: &error)
AVAudioSession.sharedInstance().setActive(true, error: &error)
}
SWIFT 3
UIApplication.shared.beginReceivingRemoteControlEvents()
self.becomeFirstResponder()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("hmmm...")
}
I'm using an instance of AVAudioPlayer to play an audio file. The app is configured to play audio in the background and an appropriate audio session is set. I am also successfully receiving remote control events.
Here's the code:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#interface ViewController ()
#property (nonatomic) AVAudioPlayer *player;
#end
#implementation ViewController
#synthesize player;
- (BOOL)canBecomeFirstResponder { return YES; }
- (void)viewDidLoad
{
[super viewDidLoad];
// Turn on remote control event delivery
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Set ourselves as the first responder
[self becomeFirstResponder];
// Set the audio session
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *setCategoryError = nil;
BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
NSError *activationError = nil;
success = [audioSession setActive:YES error:&activationError];
// Play an mp3 with AVAudioPlayer
NSString *audioFileName = #"%#/Via_Aurora.mp3";
NSURL *audioURL = [NSURL fileURLWithPath:[NSString stringWithFormat:audioFileName, [[NSBundle mainBundle] resourcePath]]];
player = [[AVPlayer alloc] initWithURL:audioURL];
[player play];
}
- (void)viewWillDisappear:(BOOL)animated {
// Turn off remote control event delivery & Resign as first responder
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
// Don't forget to call super
[super viewWillDisappear:animated];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(#"prev");
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(#"next");
break;
case UIEventSubtypeRemoteControlPlay:
[player play];
break;
case UIEventSubtypeRemoteControlPause:
[player pause];
break;
default:
break;
}
}
}
#end
When I run the app the audio plays when the view loads. The app continues to play audio when it goes into background mode. I am able to successfully pause and/or play audio from the Control Center (accessed either from within the app or the Lock Screen) but, if I access the Lock Screen audio controls and pause the player, the music pauses and the lock screen controls disappear. I expect the music to pause, but not for the controls to disappear.
In other audio apps that I use you can pause, then play, audio from the Lock Screen. Have I overlooked something? Is this a correct approach to do something like this?
You're on the right track ...
You seem to be missing setting;
MPNowPlayingInfoCenter nowPlayingInfo
Without it, you will get the results described, IE after pressing pause, the lock screen no longer shows the pause, or indeed that it is playing a song. Here's a guide on how to set it (i've taken this from working code I did some time back, but I'm sure you can figure out what's what).
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc]initWithImage:albumImage];
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = [NSDictionary dictionaryWithObjectsAndKeys:aSong.songTitle, MPMediaItemPropertyTitle,
aSong.artistName, MPMediaItemPropertyArtist, artwork, MPMediaItemPropertyArtwork, 1.0f, MPNowPlayingInfoPropertyPlaybackRate, nil];
Add this Code in ViewDidLoad() For background play. It work for me. You should try it
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
let session:AVAudioSession = AVAudioSession.sharedInstance()
do
{
try session.setCategory(AVAudioSessionCategoryPlayback)
}
catch
{
print("Background Play Error")
}
I am creating this app were there is background music playing, but I want it so the user can stop the music with a UISwitch if they dont want background music. I already have the code working for the music to play and stop (Code below) with the switch bu my question is this. When i switch to a different view (one that the switch isnt on) and the music is playing, then go back to the view. The switch is off, when i turn it back on (even thought the music is already playing), it will play it again and they will overlap each other (same music file).
Code for the switch and music player...
-(IBAction)play:(id)sender {
if (audioControlSwitch.on) {
[sound setTextColor:[UIColor blueColor]];
[sound setText:#"Sound On"];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/Tone 2.m4a", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer1.numberOfLoops = 100000000000000000;
[audioPlayer1 play];
} else {
[sound setTextColor:[UIColor darkGrayColor]];
[sound setText:#"Sound Off"];
[audioPlayer1 stop];
}
}
in yourViewController.h
#interface yourViewController : NSObject <AVAudioPlayerDelegate> {
BOOL inBackground;
}
- (void)registerForBackgroundNotifications;
in yourViewController.m
#synthesize inBackground;
#pragma mark background notifications
- (void)registerForBackgroundNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(setInBackgroundFlag)
name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(clearInBackgroundFlag)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)setInBackgroundFlag
{
inBackground = true;
}
- (void)clearInBackgroundFlag
{
inBackground = false;
}
- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)p
{
if (p.playing)
{
// Do something
}
else
{
// Do something else
}
}
#pragma mark AVAudioPlayer delegate methods
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)p successfully:(BOOL)flag
{
if (flag == NO)
NSLog(#"Playback finished unsuccessfully");
[p setCurrentTime:0.];
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
}
}
- (void)playerDecodeErrorDidOccur:(AVAudioPlayer *)p error:(NSError *)error
{
NSLog(#"ERROR IN DECODE: %#\n", error);
}
// we will only get these notifications if playback was interrupted
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)p
{
NSLog(#"Interruption begin. Updating UI for new state");
// the object has already been paused, we just need to update UI
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
}
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)p
{
NSLog(#"Interruption ended. Resuming playback");
[self startPlaybackForPlayer:p];
}
-(void)startPlaybackForPlayer:(AVAudioPlayer*)p
{
if ([p play])
{
}
else
NSLog(#"Could not play %#\n", p.url);
}
#end