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...")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With