Search code examples
iosairplayapple-tv

AirPlay to Apple TV Fullscreen


Currently, any app that I make for iPhone/iPad can be mirrored to the Apple TV via AirPlay. However, even in landscape mode, it only takes up the center part of the screen, with black on left and right sides. What all is involved with getting it to AirPlay full-screen, in the way that apps like Real Racing HD have done?

EDIT: Per a suggestion, I have added in all the code I am using, and instead of telling the secondWindow to use same root view controller as normal, I set up a new VC with a different color to see if mechanics were set up right. They are NOT, as it still just does the normal mirroring, even when telling it to use a different VC.

Here is AppDelegate.h

#import <UIKit/UIKit.h>
@class MainView;
@class ViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    UIWindow *window;
    UINavigationController *tabBarController;

}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *tabBarController;
@property (nonatomic, retain) UIWindow *secondWindow;
@end

And relevant parts of AppDelegate.m

- (void)checkForExistingScreenAndInitializeIfPresent {
    if ([[UIScreen screens] count] > 1) {
        // Get the screen object that represents the external display.
        UIScreen *secondScreen = [[UIScreen screens] objectAtIndex:1];
        // Get the screen's bounds so that you can create a window of the correct size.
        CGRect screenBounds = secondScreen.bounds;

        self.secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
        self.secondWindow.screen = secondScreen;

        // Set up initial content to display...
        NSLog(@"Setting up second screen: %@", secondScreen);
        ViewController *mainView = [[ViewController alloc] init];
        self.secondWindow.rootViewController = mainView;
        [self.secondWindow makeKeyAndVisible];

        // Show the window.
        //        self.secondWindow.hidden = NO;
    }
    NSLog(@"Screen count too low");
}

- (void)setUpScreenConnectionNotificationHandlers {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

    [center addObserver:self selector:@selector(handleScreenDidConnectNotification:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleScreenDidDisconnectNotification:)
                   name:UIScreenDidDisconnectNotification object:nil];
}

- (void)handleScreenDidConnectNotification:(NSNotification*)aNotification {
    UIScreen *newScreen = [aNotification object];
    CGRect screenBounds = newScreen.bounds;

    if (!self.secondWindow) {
        NSLog(@"Initializing secondWindow/screen in notification");
        self.secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
        self.secondWindow.screen = newScreen;

        // Set the initial UI for the window.
        ViewController *mainView = [[ViewController alloc] init];
        self.secondWindow.rootViewController = mainView;
    } else {
        NSLog(@"Second window already initialized.");
    }
}

- (void)handleScreenDidDisconnectNotification:(NSNotification*)aNotification {
    if (self.secondWindow) {
        // Hide and then delete the window.
        self.secondWindow.hidden = YES;
        self.secondWindow = nil;
    }
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window setRootViewController:tabBarController];
    [self setUpScreenConnectionNotificationHandlers];
    [self checkForExistingScreenAndInitializeIfPresent];
return YES;
}

Solution

  • What all is involved with getting it to AirPlay full-screen...

    If you want to dive right in, here is where you start learning about Windows and Screens in the iOS world: https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/WindowAndScreenGuide/UsingExternalDisplay/UsingExternalDisplay.html

    If you want to take a broader overview first, here is the aggregation page with videos and other tutorials/documentation:

    https://developer.apple.com/airplay/

    [EDIT] Here's an excerpt of code from an app of mine where I'm setting up the second display. I've taken much of this from the links I gave you. NOTE: "main" is the for the device and "remote" is for the TV.

    NOTE: This code is not production-complete; there are still state changes it does not respond to. Connect to an AirPlay Receiver and turn on Mirroring before running this.

    I have this:

    @interface AppDelegate () {
        SFCManagerMainViewController *_mainVC;
        SFCRemoteMonitorViewController *_remoteVC;
    }
    
    @end
    

    The header:

    @property (strong, nonatomic) UIWindow *window;
    @property (strong, nonatomic) UIWindow *secondWindow;
    

    and this:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [self setUpScreenConnectionNotificationHandlers];
    [self checkForExistingScreenAndInitializeIfPresent];
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    _mainVC = [[SFCManagerMainViewController alloc] initWithNibName:nil bundle:nil];
    self.window.rootViewController = _mainVC;
    [self.window makeKeyAndVisible];
    return YES;
    }
    
    - (void)checkForExistingScreenAndInitializeIfPresent {
        if ([[UIScreen screens] count] > 1) {
            // Get the screen object that represents the external display.
            UIScreen *secondScreen = [[UIScreen screens] objectAtIndex:1];
            // Get the screen's bounds so that you can create a window of the correct size.
            CGRect screenBounds = secondScreen.bounds;
    
            self.secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
            self.secondWindow.screen = secondScreen;
    
            // Set up initial content to display...
            NSLog(@"Setting up second screen: %@", secondScreen);
            _remoteVC = [[SFCRemoteMonitorViewController alloc] initWithNibName:nil bundle:nil];
            self.secondWindow.rootViewController = _remoteVC;
            [self.secondWindow makeKeyAndVisible];
    
            // Show the window.
            self.secondWindow.hidden = NO;
        }
    }
    
    - (void)setUpScreenConnectionNotificationHandlers {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    
        [center addObserver:self selector:@selector(handleScreenDidConnectNotification:)
                       name:UIScreenDidConnectNotification object:nil];
        [center addObserver:self selector:@selector(handleScreenDidDisconnectNotification:)
                       name:UIScreenDidDisconnectNotification object:nil];
    }
    
    - (void)handleScreenDidConnectNotification:(NSNotification*)aNotification {
        UIScreen *newScreen = [aNotification object];
        CGRect screenBounds = newScreen.bounds;
    
        if (!self.secondWindow) {
            NSLog(@"Initializing secondWindow/screen in notification");
            self.secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
            self.secondWindow.screen = newScreen;
    
            // Set the initial UI for the window.
            _remoteVC = [[SFCRemoteMonitorViewController alloc] initWithNibName:nil bundle:nil];
            self.secondWindow.rootViewController = _remoteVC;
        } else {
            NSLog(@"Second window already initialized.");
        }
    }
    
    - (void)handleScreenDidDisconnectNotification:(NSNotification*)aNotification {
        if (self.secondWindow) {
            // Hide and then delete the window.
            self.secondWindow.hidden = YES;
            self.secondWindow = nil;
        }
    }