How to Correctly Use the CameraRoll API on iPads

Now that iPads have built-in cameras, we have to start thinking about how to use camera-related APIs in a more cross-platform way. In particular, the CameraRoll API requires some consideration. On most devices, you can just call browseForImage on a CameraRoll instance, and trust that the right thing will happen. On iPads, however, that’s not enough.

On the iPhone and iPad touch, the image picker takes up the full screen, however on the iPad, the image picker is implemented as a kind of floating panel which points to the UI control (usually a button) that invoked it. That means browseForImage has to be a little smarter so that it knows:

  1. How big to make the image picker.
  2. Where to render the image picker.
  3. What part of the UI the image picker should point to.

AIR 3 (in beta, available on Adobe Labs) allows developers to solve this problem with the introduction of the CameraRollBrowseOptions class. CameraRollBrowseOptions allows you to specify the width and height of the image picker as well as the origin (the location of the UI component that invoked it). On platforms whose image pickers fill the entire screen, the CameraRollBrowseOptions argument is simply ignored.

Below are screenshots of a simple AIR sample application that uses the new CameraRollBrowseOptions class to tell the OS where and how to draw the image picker:

The code for the application is available on Github (or you can download the Flash Builder project file), but I’ll include the important parts here:

  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.geom.Rectangle;

  public class iPadCameraRollExample extends Sprite

    private static const PADDING:uint = 12;
    private static const BUTTON_LABEL:String = "Open Photo Picker";

    public function iPadCameraRollExample()
      this.stage.align = StageAlign.TOP_LEFT;
      this.stage.scaleMode = StageScaleMode.NO_SCALE;
      this.stage.addEventListener(Event.RESIZE, doLayout);

    private function doLayout(e:Event):void

      var topLeft:Button = new Button(BUTTON_LABEL);
      topLeft.x = PADDING; topLeft.y = PADDING;
      topLeft.addEventListener(MouseEvent.CLICK, onOpenPhotoPicker);

      var topRight:Button = new Button(BUTTON_LABEL);
      topRight.x = this.stage.stageWidth - topRight.width - PADDING; topRight.y = PADDING;
      topRight.addEventListener(MouseEvent.CLICK, onOpenPhotoPicker);

      var bottomRight:Button = new Button(BUTTON_LABEL);
      bottomRight.x = this.stage.stageWidth - bottomRight.width - PADDING; bottomRight.y = this.stage.stageHeight - bottomRight.height - PADDING;
      bottomRight.addEventListener(MouseEvent.CLICK, onOpenPhotoPicker);

      var bottomLeft:Button = new Button(BUTTON_LABEL);
      bottomLeft.x = PADDING; bottomLeft.y = this.stage.stageHeight - bottomLeft.height - PADDING;
      bottomLeft.addEventListener(MouseEvent.CLICK, onOpenPhotoPicker);
      var center:Button = new Button(BUTTON_LABEL);
      center.x = (this.stage.stageWidth / 2) - (center.width / 2); center.y = (this.stage.stageHeight / 2) - (center.height/ 2);
      center.addEventListener(MouseEvent.CLICK, onOpenPhotoPicker);
    private function onOpenPhotoPicker(e:MouseEvent):void
      if (CameraRoll.supportsBrowseForImage)
        var crOpts:CameraRollBrowseOptions = new CameraRollBrowseOptions();
        crOpts.height = this.stage.stageHeight / 3;
        crOpts.width = this.stage.stageWidth / 3;
        crOpts.origin = new Rectangle(,,,;
        var cr:CameraRoll = new CameraRoll();

Keep in mind that AIR 3 is still in beta, so you might find bugs. If you do, here’s how to file them.