Properly preventing orientation change in Flex Mobile app

Is anyone able to actually make it work properly in Flex SDK 4.6?

Here’s a short snippet :

  • Load and reload external SWF in AIR for iOS
  • Adobe AIR and iPhone - how it works?
  • Receiving Installation Error: ApplicationVerificationFailed in Flash Builder for iOS
  • ActionScript's File.upload does not work on Air SDK for iOS devices
  • Adobe Air ios packager
  • How to create an ANE that will natively display a Floating Window
  • <?xml version="1.0" encoding="utf-8"?>
    <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
            xmlns:s="library://ns.adobe.com/flex/spark" 
            addedToStage="onAddedToStage(event)"
            title="Title">
        <fx:Script>
            <![CDATA[  
                private function onAddedToStage(event:Event):void { 
                    if (stage.autoOrients) {
                        stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging, false, 0, true);
                    }
                }
    
                private function orientationChanging(event:StageOrientationEvent):void {
                    if (event.afterOrientation == StageOrientation.DEFAULT || event.afterOrientation == StageOrientation.UPSIDE_DOWN) {
                        event.preventDefault(); 
                    }     
                } 
            ]]>
        </fx:Script>
    </s:View>
    

    What I’m trying to achieve is to support Landscape mode in both orientations, so if user turns the device 180 degress, the screen should also rotate. But there should be no action at all, when user rotates the device to one of portrait orientations. Instead, I’m seeing width changes to navigator action bar and sometimes content in portrait orientations, so apparently preventing the event is not enough. I’m using the “official” way Adobe suggests, but the problem is that it’s not working very well. Granted, the stage does not change, but it seems that there’s something firing in navigator anyway, since you can see the action bar width changing.

    I had some success with explicitly setting layoutbounds to fixed width in handler method – this prevents changing the actionbar width, but it’s only a temporary solution – if the view is a subject to a transition, or some other redraw – it will again render with bad sizes. As if there was something below that was telling it that it’s in portrait mode, even though I’m trying to prevent it.

    Before you detonate with some silly ideas like “autoOrient = false”, don’t. It’s clearly not a solution for this problem. Obviously it’s a bug with Flex SDK – did anyone find a way to fix it or a stable workaround?

    EDIT: apparently others bumped into similar issue:
    – http://forums.adobe.com/message/3969531 (the main topic is about something else, but read magic robots’s comment)
    – http://forums.adobe.com/message/4130972

    2 Solutions Collect From Internet About “Properly preventing orientation change in Flex Mobile app”

    I’m not sure if this is the right one, did I do something wrong in the end, but after a lot of struggle, I’ve found this one to be stable solution:

    private function onAddedToStage(event:Event):void { 
        if (stage.autoOrients) {
            stage.removeEventListener(StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging);
            stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging, false, 100, true);
        }
    }      
    
    private function orientationChanging(event:StageOrientationEvent):void {
        event.stopImmediatePropagation();
        if (event.afterOrientation == StageOrientation.DEFAULT || event.afterOrientation == StageOrientation.UPSIDE_DOWN) {
            event.preventDefault(); 
        }   
    }  
    

    First thing to note is that addedToStage fires few times (2-3) in mobile application. I don’t know why, there’s no addChild in my code, obviously. Maybe the AIR runtime does something else. So, to avoid adding unnecessary amount of handlers, the common technique is to remove handler first – it won’t do anything, if such handler is not yet registered, but if there is, it will remove it, which will maintain the handler count on 1.

    Second thing is the priority of the event – it won’t work on 0, it has to be set on something big, to launch before stuff in AIR runtime.

    Last thing – event.stopImmediatePropagation() – now, that we’re the first to handle the event, we cant prevent this event to be sent further up in this particular scenario.

    This together makes the orientation preventing working perfectly – for me the landscape and the reversed landscape (rotated_left, rotated_right) were working and transitioning, while portrait modes did not affect the view at all.

    Now, there’s danger here – you might want to remove the listener upon leaving the view (on transition animation end, view deactivate or something), because stopImmediatePropagation will prevent the event to be handled in other parts of your application.

    I hope Adobe (or Apache now, actually) will take a closer look at this problem and trace my solution.

    EDIT

    There remaisn a last issue with this solution, which is if application starts while device is in DEFAULT or UPSIDE_DOWN orientation, in this case application will be in portrait mode.

    To fix this, a solution is to change Aspect Ratio within addedToStage handler:

       if(Stage.supportsOrientationChange) {
             stage.setAspectRatio(StageAspectRatio.LANDSCAPE);
        }
    

    So I had the same problem you had. I think I finally figured out the solution. Heres what I did:

    <s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    ...blahblahblah...
    width="1024"/>
    
    protected function tabbedviewnavigatorapplication2_applicationCompleteHandler(event:FlexEvent):void {
        stage.autoOrients=true;
        preventOrient();
    } 
    
    private function preventOrient():void { 
        if (stage.autoOrients) {
            stage.removeEventListener(StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging);
        stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging, false, 100, true);
        }
    }
    
    private function orientationChanging(event:StageOrientationEvent):void {
        if(event.afterOrientation == StageOrientation.DEFAULT || event.afterOrientation == StageOrientation.UPSIDE_DOWN || event.afterOrientation == StageOrientation.UNKNOWN) {
            event.preventDefault(); 
        }
    }
    

    Worth noting is that in the application complete handler I set stage.autoOrients to true because in the app.xml file I have it set to false, due to having a splash screen and not wanting users to re-orient the screen during that time. Really the only thing I did different is account for the StageOrientation.UNKNOWN and prevent whatever that would do, set the width to 1024(for the iPad screen, might be different for other tablet devices) in the main mxml file, and removed the stopimmediatepropagation. Hope this helps.