Wednesday, August 25, 2010

Using the External VGA Connector

One of the things that we wanted to do was plug in the external VGA adapter to a projector and demo the app to a room of people.  Sounds simple right?  Well, not really.

When using the external VGA Adapter, the device as two windows that you must add separate views to.  After an entire day attempting to capture all events from one window and attempting to pass them to the other window, I decided to take a more brute force method.  Take a screen capture every 1/4 second and display it on the external display.

The steps are below.

1. Create a UIViewController with a XIB and call it ExternalDisplayViewController.
2. In Interface Builder add a single UIImageView and set the View Mode to Aspect Fit.
3. Add a property to the ExternalDisplayViewController.h.  Don't forget to "Wire" it up in interface builder.
4. Modify the AppDelegate.h file accordingly.

#import <UIKit/UIKit.h>

@class ExternalDisplayViewController;

@interface MyAppDelegate : NSObject <UIApplicationDelegate, UIAlertViewDelegate> {
    UIWindow *deviceWindow;
 
 NSArray *screenModes;
 UIScreen *externalScreen;
 
 ExternalDisplayViewController *externalVC;

}
@property (nonatomic, retain) NSTimer *repeatingTimer;

@property (nonatomic, retain) IBOutlet UIWindow *deviceWindow;
@property (nonatomic, retain) IBOutlet UIWindow *externalWindow;

- (void) takeCapture:(NSTimer*)theTimer;

@end

5. Modify the MainWindow.xib file.  Add a second window (that will dsplayed on the external display).  Don't forget to "Wire" two windows to the new properties.

6. Add the Quartz Framework to your project and add the import line to the AppDelegate.m file.

#import <QuartzCore/QuartzCore.h>

7. Add the following code to your AppDelegate.m didFinishLaunchingWithOptions
if ([[UIScreen screens] count] > 1) {
    
   //[self log:@"Found an external screen."];
   
   // Internal display is 0, external is 1.
   externalScreen = [[[UIScreen screens] objectAtIndex:1] retain];
   //[self log:[NSString stringWithFormat:@"External screen: %@", externalScreen]];
   
   screenModes = [externalScreen.availableModes retain];
   //[self log:[NSString stringWithFormat:@"Available modes: %@", screenModes]];
   
   // Allow user to choose from available screen-modes (pixel-sizes).
   UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"External Display Size" 
            message:@"Choose a size for the external display." 
            delegate:self 
            cancelButtonTitle:nil 
            otherButtonTitles:nil] autorelease];
   for (UIScreenMode *mode in screenModes) {
    CGSize modeScreenSize = mode.size;
    [alert addButtonWithTitle:[NSString stringWithFormat:@"%.0f x %.0f pixels", modeScreenSize.width, modeScreenSize.height]];
   }
   [alert show];
   
}

8. Add the following code to your AppDelegate.m file.

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
 UIScreenMode *desiredMode = [screenModes objectAtIndex:buttonIndex];
 externalScreen.currentMode = desiredMode;
 externalWindow.screen = externalScreen;
 
 [screenModes release];
 [externalScreen release];
 
 CGRect rect = CGRectZero;
 rect.size = desiredMode.size;
 externalWindow.frame = rect;
 externalWindow.clipsToBounds = YES;
 
 externalWindow.hidden = NO;
 [externalWindow makeKeyAndVisible];
 
 externalVC = [[ExternalDisplayViewController alloc] initWithNibName:@"ExternalDisplayViewController" bundle:nil];
 CGRect frame = [externalScreen applicationFrame];
 switch(externalVC.interfaceOrientation){
  case UIInterfaceOrientationPortrait:
  case UIInterfaceOrientationPortraitUpsideDown:
   [externalVC.view setFrame:frame];
   break;
  case UIInterfaceOrientationLandscapeLeft:
  case UIInterfaceOrientationLandscapeRight:
   [externalVC.view setFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.height, frame.size.width)];
   break;
 }
 
 [externalWindow addSubview:externalVC.view];
 
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.25
   target:self selector:@selector(takeCapture:)
   userInfo:nil repeats:YES];
 self.repeatingTimer = timer;
}

- (void) takeCapture:(NSTimer*)theTimer{
 UIView *mainView = [deviceWindow.subviews objectAtIndex:0];
 
 if (mainView) {
  UIGraphicsBeginImageContext(mainView.frame.size);
  [mainView.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  
  [externalVC.imgView setImage:viewImage];
  UIGraphicsEndImageContext();
 }
 
}




Monday, August 23, 2010

Getting Started

Over the past two months myself and co-worker have been tasked with creating a working prototype of and iPad application for our company to demo at fall trade shows.  Being a complete Microsoft technology shop this has been quite an undertaking.

The goal of this blog is to share some of stuff that we struggled with the hopes that you won't have to re-invent our wheel.

The posts will not be in any particular order, as I post stuff from the past couple of months.