iOS AirPlay: my app is only notified of an external display when mirroring is ON?

Greg Maletic picture Greg Maletic · Jan 26, 2012 · Viewed 8.9k times · Source

I'm trying to enable AirPlay support in my app. I'm not doing video; I want to use the external display as a "second display".

Here's my problem: if I choose "AppleTV" from my AirPlay button, my app doesn't get notified. The only time my app -does- get notified is when I leave my app, go to the OS-level AirPlay button, choose "AppleTV" there and turn on Mirroring. If I turn mirroring off, my app is then told that the external display is gone.

So:

  1. Why doesn't my app get notified when I pick an external display from within my app?
  2. Why does my app get notified of the presence of an external display when I turn mirroring on...and not before? I'm obviously misunderstanding something, but it would seem like turning mirroring on should inform my app that the external display is gone (rather than now available, since the OS should now be using that external display for mirroring.)

Code sample below. Thanks in advance for any help!

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

     // Override point for customization after application launch.

     // Is there already an external screen?
     if (UIScreen.screens.count > 1)]
     {
          [self prepareExternalScreen:UIScreen.screens.lastObject];
     }

     // Tell us when an external screen is added or removed.
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(externalScreenDidConnect:) name:UIScreenDidConnectNotification object:nil];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(externalScreenDidDisconnect:) name:UIScreenDidDisconnectNotification object:nil];

     self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
     self.window.rootViewController = self.viewController;

     // Add AirPlay control to view controller.
     MPVolumeView* airplayButtonView = [[MPVolumeView alloc] init];
     airplayButtonView.frame = CGRectMake(300, 300, 50, 50);
     airplayButtonView.backgroundColor = [UIColor blackColor];
     airplayButtonView.showsVolumeSlider = NO;
     airplayButtonView.showsRouteButton = YES;

     [self.viewController.view addSubview:airplayButtonView];


    [self.window makeKeyAndVisible];

    return YES;
}

#pragma mark - External screen handling

- (void)externalScreenDidConnect:(NSNotification*)notification
{
     [self prepareExternalScreen:notification.object];
}

- (void)externalScreenDidDisconnect:(NSNotification*)notification
{
     // Don't need these anymore.
     self.externalWindow = nil;
}

- (void)prepareExternalScreen:(UIScreen*)externalScreen
{
     NSLog(@"PREPPING EXTERNAL SCREEN.");
     self.viewController.view.backgroundColor = [UIColor blueColor];
     CGRect frame = externalScreen.bounds;
     self.externalWindow = [[UIWindow alloc] initWithFrame:frame];
     self.externalWindow.screen = externalScreen;
     self.externalWindow.hidden = NO;
     self.externalWindow.backgroundColor = [UIColor redColor];
}

Answer

quellish picture quellish · Jan 27, 2012

That's correct, unfortunately. The secondary display (the airplay screen) is only available with mirroring.

Here is an application that shows how to implement this: https://github.com/quellish/AirplayDemo

Looking at your code, you SHOULD be getting the UIScreenDidConnectNotification when a user goes to the airplay menu and turns on mirroring while your app is active. The "Airplay Button", wether a MPVolumeView or movie controller, does not control mirroring (and thus the external display functionality). Video and audio out are unfortunately separate from mirroring, and mirroring can only be turned on or off using the system wide mirroring UI.

Bottom line: You can't turn on that AirPlay screen from within your app.