Headset Button Press not recognized when in secondary views of iOS application - iphone

I have an iOS application that streams music using RadioKit SDK. The audio plays fine while switching between different views of a tab bar controller, however when a headset is plugged in and the user is viewing one of the secondary tab views (there are 5 total, 1 primary for when the app launches, and 4 others) if they try to hit the play/pause button the app does not recognize the action. If it's playing, it won't pause, if it's paused, it won't play. The app does, however, recognize volume changes from the headset.
This behavior is consistent whether the app is in view or if in background or if the device is locked.
I've done some heavy searching and can't seem to figure this out. Help is appreciated, thank you!
iOS version 6.1.3
iOS SDK version 6.1
xCode version 4.6.3

You want to listen to -(void)remoteControlReceivedWithEvent:(UIEvent *)event
Make a base UIViewController, let's call it BaseViewController and add the following :
-(void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
switch(event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
break;
case UIEventSubtypeRemoteControlPlay:
break;
case UIEventSubtypeRemoteControlPause:
break;
case UIEventSubtypeRemoteControlStop:
break;
default:
break;
}
}
else{
[super remoteControlReceivedWithEvent:event];
}
}
Also you want to include the following, in the base class, so that you can actually receive any remote control events.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIApplication *application = [UIApplication sharedApplication];
if ([application respondsToSelector:#selector(beginReceivingRemoteControlEvents)]) {
[application beginReceivingRemoteControlEvents];
}
[self becomeFirstResponder];
}
And finally have all the UIViewControllers be subclass of the BaseViewController.

Related

Status Bar Still Showing

I am getting REALLY frustrated!!
I have tried every living possibility to get rid of the UIStatusBar at the top of my app...
I have tried:
Setting Status Bar to "None" in IB
Running [[UIApplication sharedApplication] setStatusBarHidden:YES]; on application launch AND in each scene.
Going to the .plist and changing the value for Status Bar Hidden at Startup: YES
Setting that same value on the home page for the target
Setting - (BOOL)prefersStatusBarHidden
{
return YES;
} in the app delegate
Literally none of this works... It still shows up on all of my views, and it is SUPER frustrating
Thanks again :)
Side note: I'm in xcode 5, developer beta iOS 7 beta 6, but this also happens on my old ios6 and xcode 4 apps
Please try this
//viewDidload
if ([self respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)]) {
// iOS 7
[self prefersStatusBarHidden];
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
} else {
// iOS 6
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}
// Add this Method
- (BOOL)prefersStatusBarHidden
{
return YES;
}
This code has been taken from this link
What I usually do is add two key-value properties to the Info.plist file.
The properties source code is:
You need to add a method to the view controller, and not to the app delegate as you write.
- (BOOL)prefersStatusBarHidden
{
return YES;
}
As something occured to me!!
for anyone else ,,
Make sure you are modifying the info.plist in the right *TARGET* :/
plus the accepted answer.

(iOS) How do I check an iPhone's paired Bluetooth devices?

I want to use CoreBluetooth (as my app must be eligible for the App Store) to check all of the currently paired and connected Bluetooth devices. This should not require any Bluetooth scanning, right? I just want to see what the system is paired with. If I can't do that, a scan is the second option.
What I'm trying is not working. It says that Bluetooth is not powered on and crashes, but the CBCentralManager's state is on! Any ideas on how to fix this, or am I totally off track?
All of this in ViewController for now:
- (void)viewDidLoad{
[super viewDidLoad];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
[self.centralManager retrieveConnectedPeripherals]; //makes the system call didRetrieveConnectedPeripherals
}
- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals{
NSLog(#"didRetrieveConnectedPeripherals called");
for (CBPeripheral *a in peripherals){
NSLog(a.name); //just log the name for now to see if it recognized it
} //but it never ends up logging anything, and I have a BT keyboard paired/connected with the iPhone 5
} //and I get an error at some point, <CBConcreteCentralManager: 0x71ab020> is not powered on
- (void)centralManagerDidUpdateState:(CBCentralManager *)manager{
if ([manager state] == CBCentralManagerStatePoweredOff) NSLog(#"CBCentralManagerStatePoweredOff");
if ([manager state] == CBCentralManagerStatePoweredOn) NSLog(#"CBCentralManagerStatePoweredOn"); //this is what gets logged when I run it on an iPhone 5
if ([manager state] == CBCentralManagerStateResetting) NSLog(#"CBCentralManagerStateResetting");
if ([manager state] == CBCentralManagerStateUnauthorized) NSLog(#"CBCentralManagerStateUnauthorized");
if ([manager state] == CBCentralManagerStateUnknown) NSLog(#"CBCentralManagerStateUnknown");
if ([manager state] == CBCentralManagerStateUnsupported) NSLog(#"CBCentralManagerStateUnsupported");
}
I've been working on this also, and hopefully some of what I've learned will help.
Couple of things:
1) You're likely getting the "Bluetooth not powered on" error because you're calling [self.central retrieveConnectedPeripherals] immediately after initializing the CBCentralManager. You need to give the CBCentralManager enough time to connect to the bluetooth hardware.
Try this instead,
- (void)viewDidLoad{
[super viewDidLoad];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)manager {
switch (manager.state) {
case CBCentralManagerStatePoweredOn:
NSLog(#"CBCentral Manager powered on");
[self.centralManager retrieveConnectedPeripherals];
break;
case CBCentralManagerStatePoweredOff:
...etc.
}
Also, be aware that CBCentralManager works for iOS devices with Bluetooth 4.0. Currently, Bluetooth 4 is installed on iPhone 4S/5, iPod 4, and iPad 3/4/mini. So, be aware that your App will not work on iPad 1/2, iPhone 2/3, and iPod 2/3.
Still figuring this out, but it is also possible that you may not see your keyboard because it is not a Bluetooth 4 device. Let me know what you find out.

How to assign AVPlayer play/pause button iOS?

I have asked this question before few days but nobody answer me and I could not find the solution.
So I want to ask the same question again and please answer me if you know the answer.
I want to assign these buttons in the picture to my avplayer play/pause button.
Note: My application icons appear in the now playing bar instead of Music icon, my application works fine in background .
Any help please.
To allow delivery of remote-control events, you must call the beginReceivingRemoteControlEvents method of UIApplication.
You then respond to the remote control events by implementing the remoteControlReceivedWithEvent: method like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[UIApplication sharedApplication] becomeFirstResponder];
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
switch (event.subtype)
{
case UIEventSubtypeRemoteControlPlay:
// play code
...
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
// toggle code
...
break;
case UIEventSubtypeRemoteControlNextTrack:
// next code
...
break;
default:
break;
}
}
Note:
"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 (note: beginReceivingRemoteControlEvents) when using the shared command center object."
(Source)

Is there any notification for detecting AirPlay in Objective-C?

I am using MPVolumeView for showing Airplay icon and it works fine.
But I need to show an animation when Airplay network comes, and hide that animation when airplay network hides.
Is there a notification that will let me know when Airplay starts and ends?
This is exactly what you're looking for - https://github.com/StevePotter/AirPlayDetector
It is a single class that provides a property to determine whether airplay devices are active. And a notification when availability changes.
Using it is simple. Like, to determine availability you write:
[AirPlayDetector defaultDetector].isAirPlayAvailable
Enjoy!
To be precise:
To check exactly for airplay with public API: NO
All you can do with public API is to check for available wireless routes, which includes airplay in it: (In simple case when you have a MPVolumeView instance hooked up somewhere to your view, you can just call volumeView.areWirelessRoutesAvailable;)
If you are curious how to check if exactly airplay is available with private API:
- (BOOL)isAirplayAvailable
{
Class MPAVRoutingController = NSClassFromString(#"MPAVRoutingController");
id routingController = [[MPAVRoutingController alloc] init];
NSArray* availableRoutes = [routingController performSelector:#selector(availableRoutes)];
for (id route in availableRoutes) {
NSDictionary* routeDescription = [route performSelector:#selector(avRouteDescription)];
if ([routeDescription[#"AVAudioRouteName"] isEqualToString:#"AirTunes"])
return true;
}
return false;
}
(And in fact MPVolumeView has an MPAVRoutingController instance as its ivar, so the -areWirelessRoutesAvailable is just an accessor exactly for [volumeView->_routingController wirelessDisplayRoutesAvailable])
Also AVAudioSession exposes currentRoute to you, so you do can check if airplay is active easily with:
- (BOOL)isAudioSessionUsingAirplayOutputRoute
{
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
AVAudioSessionRouteDescription* currentRoute = audioSession.currentRoute;
for (AVAudioSessionPortDescription* outputPort in currentRoute.outputs){
if ([outputPort.portType isEqualToString:AVAudioSessionPortAirPlay])
return true;
}
return false;
}
(the answer about AirPlayDetector doesn't guarantee that Airplay is available - all it does it checks the alpha value of MPVolumeView's routeSelection button, which will be shown in any case when wireless routes are available, bluetooth for example. It will do exactly the same as volumeView.areWirelessRoutesAvailable;)
There's a MPVolumeViewWirelessRoutesAvailableDidChangeNotification since iOS 7 you can register for.
It can be done much easier with ReactiveCocoa. Check it out:
MPVolumeView *myVolumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(0, 0, 180, 22)];
for (UIView *view in myVolumeView.subviews) {
if ([view isKindOfClass:[UIButton class]]) {
[[RACAbleWithStart(view, alpha) distinctUntilChanged] subscribeNext:^(id x) {
NSLog(#"airplay button visibility changed %#", x);
}];
[[RACAbleWithStart(view, frame) distinctUntilChanged] subscribeNext:^(id x) {
NSLog(#"airplay button connection changed %#", x);
}];
}
}
6 years later.
I think Sankar Siva did not ask for detecting, but for activating an airplay route.
I've upped #Alf because he placed me on the right direction, but he is not answering to the question.
MPVolumeViewWirelessRoutesAvailableDidChangeNotification fires when MPVolumeView detects a new route.
On the other hand, MPVolumeViewWirelessRouteActiveDidChangeNotification fires when a new route is taken, eg: when you select your Apple TV for example.
No need of private API.
If you want a notification here is the way to do it
[[NSNotificationCenter defaultCenter]
addObserver:self
selector: #selector(deviceChanged:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];
- (void)deviceChanged:(NSNotification *)sender {
NSLog(#"Enters here when connect or disconnect from Airplay");
}

Detect user input using headsets

I'm trying to detect user input (a click) on the headphones connected to an iPhone. So far I've only found how to detect interruptions using AVAudioSession. Is AVAudioSession right or is there another way? how?
You want this:
beginReceivingRemoteControlEvents
You implement something this in one of your VCs classes:
// If using a nonmixable audio session category, as this app does, you must activate reception of
// remote-control events to allow reactivation of the audio session when running in the background.
// Also, to receive remote-control events, the app must be eligible to become the first responder.
- (void) viewDidAppear: (BOOL) animated {
[super viewDidAppear: animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (BOOL) canBecomeFirstResponder {
return YES;
}
// Respond to remote control events
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[self playOrStop: nil];
break;
default:
break;
}
}
}
See the sample code here.
It is even easier now, as of iOS 7. To execute a block when the headphone play/pause button is pressed:
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(#"toggle button pressed");
return MPRemoteCommandHandlerStatusSuccess;
}];
or, if you prefer to use a method instead of a block:
[commandCenter.togglePlayPauseCommand addTarget:self action:#selector(toggleButtonAction)];
To stop:
[commandCenter.togglePlayPauseCommand removeTarget:self];
or:
[commandCenter.togglePlayPauseCommand removeTarget:self action:#selector(toggleButtonAction)];
You'll need to add this to the includes area of your file:
#import MediaPlayer;