user717452
user717452

Reputation: 111

Mirror ViewController to External Display

I am trying to in code, get my view on the iPad app to mirror to an External Display. In AppDelegate didFinishLaunching, I have:

 if ([[UIScreen screens] count] > 1)
    {
        UIScreen *secondScreen = [[UIScreen screens] objectAtIndex:1];
        NSString *availableModeString;

        for (int i = 0; i < secondScreen.availableModes.count; i++)
        {
            availableModeString = [NSString stringWithFormat:@"%f, %f",
                                   ((UIScreenMode *)[secondScreen.availableModes objectAtIndex:i]).size.width,
                                   ((UIScreenMode *)[secondScreen.availableModes objectAtIndex:i]).size.height];

            [[[UIAlertView alloc] initWithTitle:@"Available Mode" message:availableModeString delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
            availableModeString = nil;
        }

        // undocumented value 3 means no overscan compensation
        secondScreen.overscanCompensation = 3;

        self.secondWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 1280, 720)];
        self.secondWindow.backgroundColor = [UIColor blueColor];
        self.secondWindow.screen = secondScreen;

        ViewController *viewController = [[ViewController alloc] init];
        self.secondWindow.rootViewController = viewController;

        self.secondWindow.hidden = NO;
    }

All that shows up in the external display is the blue background color set in code.

Upvotes: 3

Views: 1614

Answers (2)

Abhishek T.
Abhishek T.

Reputation: 1133

First checks for the presence of an external display at app startup. If a second display is available, it creates a window for it. I used following code in my app.

(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...
        // Show the window.
        self.secondWindow.hidden = NO;
    }
}

Thien register for connection and disconnection notifications by following code.

(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];
}

Handling screen connection and disconnection notifications

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

    if (!self.secondWindow)
    {
        self.secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
        self.secondWindow.screen = newScreen;

        // Set the initial UI for the window.
    }
}

(void)handleScreenDidDisconnectNotification:(NSNotification*)aNotification
{
    if (self.secondWindow)
    {
        // Hide and then delete the window.
        self.secondWindow.hidden = YES;
        self.secondWindow = nil;

    }

}

If you don’t create a window for the display—or if you create a window but don’t show it—a black field is displayed on the external display.

There’s no guarantee that all modes will be available in an external display, so you should not depend on the availability of a specific mode.

In rare cases, you might want to use a different value for the overscanCompensation property, but doing so always results in more work that you have to do. For example, if you use UIScreenOverscanCompensationInsetBounds, you must be prepared to handle the bounds of nonstandard display sizes.

Upvotes: 0

quad16
quad16

Reputation: 204

I can mirror a view on my iPhone 6 using this:

@interface MirrorViewController ()

@property (nonatomic, retain) UIView *viewToMirror;
@property (nonatomic, retain) UIView *snapshotView;

@end

@implementation MirrorViewController

- (instancetype)initWithViewToMirror:(UIView*)view
{
    self = [super initWithNibName:nil bundle:nil];
    if (self == nil) return nil;
    self.viewToMirror = view;
    return self;
}

- (void)viewDidLoad
{
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)update
{
    [self.snapshotView removeFromSuperview];
    self.snapshotView = [self.viewToMirror snapshotViewAfterScreenUpdates:NO];
    [self.view addSubview:self.snapshotView];
}

@end

Here's how you could use it in your didFinishLaunching code:

if ([[UIScreen screens] count] > 1)
{
    UIScreen *secondScreen = [[UIScreen screens] objectAtIndex:1];

    // [...]

    self.secondWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 1280, 720)];
    self.secondWindow.backgroundColor = [UIColor blueColor];
    self.secondWindow.screen = secondScreen;

    MirrorViewController *mirrorViewController = [[MirrorViewController alloc] initWithViewToMirror:self.window.rootViewController.view];
    self.secondWindow.rootViewController = mirrorViewController;

    self.secondWindow.hidden = NO;
}

I haven't tested it on an iPad with external display though because i don't have the gear.

Upvotes: 1

Related Questions