Unable to handle remote control events for backgronud audio - ios

In the view controller for my audio player I've added this:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
This switches the icon in the player controls from the iPod icon to my own app, it also puts the little playback icon in the status bar.
Next I added this to my view controller to handle the remote events:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
NSLog(#"REMOTE EVENT!");
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[streamer pause];
break;
case UIEventSubtypeRemoteControlPlay:
[streamer start];
break;
case UIEventSubtypeRemoteControlPause:
[streamer pause];
break;
case UIEventSubtypeRemoteControlStop:
[streamer stop];
break;
default:
break;
}
}
However, this is never called. I tried bringing up the playback controls while the app was running, I tried going back to my home screen and tapping some playback controls, and I tried my earbuds' controls. All had no luck.
Does anyone have any pointers as to where I can be going wrong?
Thanks.

You have implemented the wrong method: instead of - (void) viewWillAppear:(BOOL)animated it should have been - (void)viewDidAppear:(BOOL)animated.

I ran into this problem before and discovered that in my problem it was because my view was a subview and the parent was catching the remote events. I would start in the AppDelegate and implement the method and see if anything is being sent to the AppDelegate and then trace it down from there. Chances are there is a view or viewController that is catching the event and not passing it along.

Have you made sure to set your audio session? Use AVAudioSession and set the category to AVAudioSessionCategoryPlayback before becoming first responder.

Related

How to add rewind buttons to lock screen?

I have realized representation of my AVPlayer on iPhone lock screen via MPNowPlayingInfoCenter. But I can't found how to add ±15 seconds rewind buttons like in standard Music app.
So the question is How to add this buttons on lock screen?
I'm using AVAudioPlayer at the moment, but the remote controlling method which is - (void)remoteControlReceivedWithEvent:(UIEvent *)event must not be involved with the type of the player you're using.
Follow this:
In your view controller's viewDidLoad method add the following code:
//Make sure the system follows our playback status - to support the playback when the app enters the background mode.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
Then add these methods:
viewDidAppear:: (if not implemented already)
- (void)viewDidAppear:(BOOL)animated {
[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];
}
viewWillDisappear: (if not implemented already)
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
//End recieving events
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
And:
//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
//if it is a remote control event handle it correctly
if (event.type == UIEventTypeRemoteControl)
{
if (event.subtype == UIEventSubtypeRemoteControlPlay)
{
[self playAudio];
}
else if (event.subtype == UIEventSubtypeRemoteControlPause)
{
[self pauseAudio];
}
else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause)
{
[self togglePlayPause];
}
else if (event.subtype == UIEventSubtypeRemoteControlBeginSeekingBackward)
{
[self rewindTheAudio]; //You must implement 15" rewinding in this method.
}
else if (event.subtype == UIEventSubtypeRemoteControlBeginSeekingForward)
{
[self fastForwardTheAudio]; //You must implement 15" fast-forwarding in this method.
}
}
}
This is working fine in my app, however if you want to be able to receive remote control events in all view controllers, then you should set it in the AppDelegate.

App Icon won't show in music controls in app switcher

I'm writing an app that streams music and I'm having a ton of trouble setting the icon on the music control dock screen (when you double-click the home button and swipe to the left). All the documentation said to do something like this, but the icon never appears and I don't ever receive the event notification. All of this code is in my player view controller. _radioPlayer is an instance of an AVQueuePlayer. What am I doing wrong here?
- (void)viewWillAppear:(BOOL)animated
{
UIApplication *application = [UIApplication sharedApplication];
if([application respondsToSelector:#selector(beginReceivingRemoteControlEvents)])
[application beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[_radioPlayer pause];
break;
case UIEventSubtypeRemoteControlPlay:
[_radioPlayer play];
break;
case UIEventSubtypeRemoteControlPause:
[_radioPlayer pause];
break;
default:
break;
}
}
EDIT: I read through the documentation and followed the steps but it's still not working. Here are some of the possible reasons I've come up with:
A. My music isn't playing yet when viewDidAppear is called. In the documentation it states
"Your app must be the “Now Playing” app. Restated, even if your app is the first responder >and you have turned on event delivery, your app does not receive remote control events until >it begins playing audio."
I tried calling beginReceivingRemoteControlEvents: and becomeFirstResponder: after the music starts playing but this doesn't work either. Can these only be called in viewDidAppear:? Does iOS automatically detect when the music begins playing so this isn't necessary?
B. There is something weird about using an AVQueuePlayer. I'm almost positive this isn't it since the event message is handled by the view controller, not the player itself.
It may not working for you because you are calling beginReceivingRemoteControlEvents in viewWillAppear instead of viewDidAppear. Check out the documentaion -
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Turn on remote control event delivery
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}

Remote control events in UITabBar to control play

This is a similar question to this. But I still can't quite figure out what to do.
So, I have a tabbar app that functions similar to an ipod. One view controller is the "NOW PLAYING" view controller and it is the view controller at index 1. So, in that VC I have:
- (void)viewDidAppear:(BOOL)animated
{
// Turn on remote control event delivery
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
// Set itself as the first responder
[self becomeFirstResponder];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
NSLog(#"Where is my event?");
if(event.type == UIEventTypeRemoteControl)
{
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
NSLog(#"Pause");
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(#"Next");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(#"Prev");
break;
default:
NSLog(#"SOMETHING WAS CLICKED");
break;
}
}
}
I don't receive any events on remote clicks either on the headphones or on the double-home button click shortcuts. I am running on an actual iPhone, not in the simulator. I am using a AVQueuePlayer (which IS playing) to manage my media.
I just shoved everything into the AppDelegate and told the AppDelegate to call to the ViewController to tell it what to do and it worked fine. Is it poor practise to put it in the AppDelegate?

What would cause a view controller to lose firstResponder status?

In my parent view controller I have this:
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"BECOMING FIRST RESPONDER");
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
In any child view controller that I want to handle remote control events I do this:
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
{
//do something
break;
}
default:
break;
}
}
}
The problem is that the events are received in one of the child classes but not the other, even though they are very similar. They are presented the same way. Neither of them override viewDidAppear so they should both become first responder when they appear and receive remote control events.
So my question is, what could be making the one view controller lose first responder status?
EDIT:
Some debugging logs show that the view controller actually is the first responder. But for some reason it's not receiving the events.
Try the following:
[self resignFirstResponder];

iPhone headset play button click

I want to detect headset button click on iPhone, see Detect headset button click on iPhone SDK.
I follow as the http://developer.apple.com/library/IOS/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/RemoteControl/RemoteControl.html,
then I wrote code as below, but it cannot work!!
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent
{
if (receivedEvent.type == UIEventTypeRemoteControl) {
NSLog(#"Remote Control Event");
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
NSLog(#"UIEventSubtypeRemoteControlTogglePlayPause");
break;
default:
break;
}
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
My Problem is that I cannot catch remote control event. When play or pause button on headset clicked, console prints nothing! Who can help me? Thank you.
Is your Info.plist updated to indicate that you support audio?
From http://www.iphonedevsdk.com/forum/iphone-sdk-development/44433-there-way-respond-clicks-headphone-buttons.html
Add the new line, and select "Required Background Modes". In the "Item 0" that appears under/next to it, select "App plays audio".

Resources