Opt In Image
Making your own extension?

Fill in your details below and get a free sample of the Easy Native Extensions eBook.

  • step-by-step guide to making your iOS extension in under an hour
  • tutorials
  • infographics
  • example code

April 2014 update: we have moved the project and packaged it with a sleek manual on our new website, along with a sleek manual and demo video. Check it out: http://easynativeextensions.com/product/diadraw-e-mail-ane/

News: The post below explains a workaround for a problem, which caused applications built with Adobe AIR 3.2 or lower to forcibly change their orientation, when using a native extension with a native window on iOS, like the Mail Composer.

If you are building with Adobe AIR 3.3 or newer and just need a native extension for e-mail, have a look at this new post:  E-mail native extension updated for Adobe AIR 3.4 and iOS 6.0

If I have sinned towards Adobe or Apple, I swear it wasn’t premeditated. For the last couple of weeks however I have been paying for sins I must have committed and that, I hope, has taught me stuff.

The problem

AIR mobile application, iOS native extension for sending e-mail.

Everything works hunky-dory, until you close the e-mail client. Then the app rotates, although you haven’t rotated the device. Sound familiar?

I finally found a workaround that works for me and got flash.display.Stage and MFMailComposeViewController to play nicely together. So I’m thinking of popping my head out and offering my twopence to the community.


This could be a looooong post or a short one, depending on how deep you choose to go down the rabbit hole.

The workaround

For those of you who would rather cut to the chase, here is what worked for me.

There is a link to an example project at the end of the post, based on Piotr Kościerzyński‘s original project for in-app mail composer. Here is the summary in three steps:

  1.  Subclass the native view controller  in your native code (MFMailComposeViewController in my case) and override
    <br />- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;<br />

    so you can control how the native view orients itself.

  2. Add a category to UIViewController in your native code and implement these two methods:
    <br />#import<br />@interface UIViewController (Orientation)<br /><%%KEEPWHITESPACE%%>        - ( UIInterfaceOrientation ) _preferredInterfaceOrientationGivenCurrentOrientation : ( UIInterfaceOrientation ) interfaceOrientation;<br /><%%KEEPWHITESPACE%%>        - (BOOL) _isSupportedInterfaceOrientation : ( UIInterfaceOrientation ) interfaceOrientation;<br />@end<br />

    This will let you define the behaviour for most of your views, including the ones implemented in Actionscript.

  3. Add a listener for StageOrientationEvent.ORIENTATION_CHANGING in your Actionscript code and make sure you catch it at the capture phase with high priority. This will let you stop undue reorienting while your native view lives. Make sure you stop listening to the event or stop preventing it from causing reorientation afterwards.
<br />stage.addEventListener( StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging, true, 99 );<br />...<br />private function orientationChanging( _event : StageOrientationEvent ) : void<br />{<br /><%%KEEPWHITESPACE%%>           if ( _event.cancelable )<br /><%%KEEPWHITESPACE%%>           {<br /><%%KEEPWHITESPACE%%>                      _event.preventDefault();<br /><%%KEEPWHITESPACE%%>           }<br />}<br />

Why workaround and not a solution?

Step 2 enforces the same orientation behaviour on all views in the application, so if you need some of your views to be able to orient themselves in certain ways and others - not, you might need a different approach.

Lenghty and potentially boring rant about how we got there

For you, kind reader, who wouldn't mind lending me a sympathetic ear, this is my tale.

First of all, I would like to offer my thanks to  Piotr Kościerzyński for his post iOS native extension for Adobe AIR. In-app mail composer and the example he provided. Although I have rewritten most of it to fit my needs,  the code I am offering below is based on his example.

I was glad that someone somewhere had already blazed the way for us, when I found the example a month ago. It seemed a bit bizarre that the app would reorient itself after having shown the mail composer, but I didn't think much of it at the time. It looked rather an innocent issue and I was certain it wouldn't take long to fix, so I left it as a desert, to have a look at after everything else was ready for a release.

Little did I know that I would have to put my boxing gloves on for almost two weeks and quite a few rounds...

Round 1: stage.autoOrients, run-time

Don't laugh. I was young and naive. I thought that if I disabled the auto-orientation of the Actionscript stage before I launched the mail composer and enabled it after it had gone, my problem would be solved.

No such luck. I noticed the app would actually start rotating while the mail composer was popping up. Spooky...

Adobe & Apple : me = 1 : 0

Round 2: stage.autoOrients, package-time

By package-time I mean the application descriptor in the Flex project: MyApp-app.xml:

<br />false<br />

Now, this was a bit silly, because we want our app to be able to orient itself in every way possible, but I was curious. Surely this should stop the app from rotating at all, even after I dismiss the mail client, right?

Wrong.

Apparently Apple's stuff works its magic around this and we still get reoriented. And stay like this, until we show the mail client again.

Adobe & Apple : me = 2 : 0

Round 3: StageOrientationEvent.ORIENTATION_CHANGING

stage.autoOrients = false, in theory, stops any StageOrientationEvent being dispatched. On the other hand, if we want to allow auto orientation sometimes, but stop it at other times, listening to this event before orientation changes occur lets us do just that:

<br />stage.addEventListener( StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging );<br />

So I thought I'd stop the event from completing its mission while the mail composer lived:

<br />private function orientationChanging( _event : StageOrientationEvent ) : void<br />{<br /><%%KEEPWHITESPACE%%>           if ( _event.cancelable )<br /><%%KEEPWHITESPACE%%>           {<br /><%%KEEPWHITESPACE%%>                      _event.preventDefault();<br /><%%KEEPWHITESPACE%%>           }<br />}<br />

There were good news and bad news...

The good news: all of the StageOrientationEvent.ORIENTATION_CHANGING events I was intercepting were cancellable.

The bad news: the stage was still being oriented in a funny way after the mail composer was dismissed!

Adding another listener, this time for StageOrientationEvent.ORIENTATION_CHANGE showed me that a relentless ORIENTATION_CHANGE event was sent, informing me that the last orientation attempt succeeded, despite my kicking and screaming and trying to prevent it from happening.

Wonderful.

Adobe & Apple : me = 3 : 0

Round 4: set stage.orientation manually

I am Dyslexia of Borg. Resistance is futile. Your ass will be laminated.

If you are going to force an orientation on me, I'll retaliate by setting the orientation back to what I need it to be. With my bare hands.

I learnt a couple of things during this round:

  • don't mess with stage.setOrientation
  • don't trust stage.deviceOrientation

I thought that, after we've been wrongly (do you hear the indignation?) reoriented, I'd check what orientation the device was in and quietly tell the stage to get itself in order, like Adobe kindly tell us we can do. Well, that kind of worked. Sometimes.

To be fair to Adobe, they do warn that stage.deviceOrientation can sometimes be unknown, for example, when first start the app (until we manually shake or rotate the device, I found) or when the device is lying flat.

But that was not my biggest problem. My sneakily setting the stage orientation usually paid off the first time I did it in any given device orientation. Once the mail composer had been dismissed however and that had done its dirty work, my stage ended up physically reorienting and/or resizing itself, but not knowing it. In other words, the stage.orientation property would politely report the last orientation I had manually set it to, although it was neither oriented, nor sized for what it said it was.

Adobe & Apple : me = 4 : 0

Round 5: override MFMailComposeViewController

I debug my code on an iPhone most of the time, so was for a while oblivious to the fact that on an iPad the mail composer itself would sometimes start in the wrong orientation too. That is, until my brother, who's the iPad guy, became loud enough about it, so I couldn't ignore it any more.

Thankfully, that was at least straightforward a fix, outlined in the Apple manual:  Why won't my UIViewController rotate with the device?

So, in the native code, I subclassed MFMailComposeViewController and added an override for

<br />- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;<br />

which would return YES for any orientation.

I then saw that confirmed in Aral Balkan's blog and, actually, in the comments in Piotr's blog (and I thought I had read carefully the first few times, pah!)

The rotation after the mail composer gracefully left the stage (excuse the pun) still remained a problem, but hey:

Adobe & Apple : me = 4 : 1

Round 6: an attempt to get Flex and Cocoa to play together

Once I had crossed over to the Native side, I decided I may as well explore a bit.

The native code gets a non-native view controller to display the mail client's view and that view controller, non-native though it might be, must implement an interface that the native can talk to:

<br />[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentModalViewController:mailComposer animated:YES];<br />

Now that rootViewController, which is a UIViewController I reckoned would control either my Actionscript spark.components.View or flash.display.Stage. I thought of asking it a few questions and called shouldAutorotateToInterfaceOrientation on it.

Turned out that our little rootViewController changes its mind about whether it shouldAutorotateToInterfaceOrientation, depending on the current orientation: it returns YES for everything else and NO for the orientation it is already in.

At this point it seemed like the dialogue between Apple's and Adobe's classes went like this:

Stage. Curtains rising. Orientation: UIInterfaceOrientationPortrait.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Apple: "State your preferred orientation. Do you support UIInterfaceOrientationPortrait?"

Adobe: "Well, I am already oriented in Portrait, so I see no reason to be reoriented to it again. So, NO."

Apple: "Check again. shouldAutorotateToInterfaceOrientation( UIInterfaceOrientationPortrait )?"

Adobe: "No."

Apple: "Well, this fella here is telling me we should be oriented UIInterfaceOrientationPortrait, but if you don't support it... What do you support, then? shouldAutorotateToInterfaceOrientation( UIInterfaceOrientationLandscapeLeft )?"

Adobe: "Well, as a matter of fact, yes. But I don't see why I should..."

Apple: "Too late. You have now been oriented to UIInterfaceOrientationLandscapeLeft. Resize your troops."

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Spooky music. Curtains.

Seriously, I'm not making this up. It's a transcript from a couple of stack traces of where the mail composer gets reoriented - I won't bore you with the long one:

<br />#0 0x008482be in -[MailCoposeViewControllerWrapper shouldAutorotateToInterfaceOrientation:]<br />#1 0x33cb1e30 in -[UINavigationController _isSupportedInterfaceOrientation:] ()<br />#2 0x33ce337e in -[UIViewController _preferredInterfaceOrientationGivenCurrentOrientation:] ()<br />#3 0x33d5105a in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:] ()<br />#4 0x33d504ea in -[UIViewController presentViewController:withTransition:completion:] ()<br />#5 0x33dab82a in -[UIViewController presentModalViewController:animated:] ()<br />

And, as if I needed more proof, I noticed a grumpy comment in the XCode's output window while debugging, which was a response to my stopping the ORIENTATION_CHANGING event in Round 3:

<br />The view controller  returned NO from -shouldAutorotateToInterfaceOrientation: for all interface orientations. It should support at least one orientation.

Just to make sure, I checked what rootViewController would say if I asked it what class it was and there I had it:

(lldb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] class]<br />(id) $0 = 0x008e1dac CTStandaloneViewController

It looked like my rootViewController must be masquerading as CTStandaloneViewController, a descendant of UIViewController... So, if I could get this CTStandaloneViewController to answer nicely to shouldAutorotateToInterfaceOrientation, my problems would be solved!

I started reading about categories, extensions and swizzling... The last one seemed like it might cause more problems than it solved, namely a rejection from the App Store.

At this point I realised that I hadn't been out of my dark cave for days, was out of tuna tins, in need of a hair cut and had messages on my phone from friends checking whether I was still alive. I put the phone aside with a grunt, promising to myself to respond to everyone later and continued to follow the lead on that CTStandaloneViewController guy.

He had left a fingerprint on my crime scene and I could put him there.  My problem was that the XCode's compiler, when dragged to the interrogation room, kept shaking its head and claiming that it had never seen, spoken to or even heard of any CTStandaloneViewController...

Adobe & Apple : me = 5 : 1

Round 7: tickle the stack trace

I had gone too far to give up at that point, so, just for the sake of it, I went a bit mad and messed with whatever calls seemed relatively safe on the stack traces I could see whenever the mail composer was asked about its orientation.

Added categories to UIViewController and UINavigationController, where I made implementations of most methods that had to do with orientation:

<br />UIViewController _preferredInterfaceOrientationGivenCurrentOrientation<br />UIViewController shouldAutorotateToInterfaceOrientation<br />UINavigationController _isSupportedInterfaceOrientation<br />

etc. etc.
Unsurprisingly,

<br />UIViewController shouldAutorotateToInterfaceOrientation<br />

and the like didn't make much difference as far as my rootViewController was concerned, because its type, the infamous CTStandaloneViewController must be overriding these, rather than use the implementations in UIViewController. The methods starting with an underscore however (for 'private'?), seemed to be a different story.

In the end, I got a combination of two of them that tamed my rootViewController:

_preferredInterfaceOrientationGivenCurrentOrientation

and

_isSupportedInterfaceOrientation

of UIViewController.
Got these to agree with whatever orientation was suggested and... look, ma, no reorientation!

Adobe & Apple : me = 5 : 2

Round 8: involuntary resizing?

But wait, that's not all! Now, when I closed the mail composer, the stage would stay oriented the way I wanted it to, but it would occasionally resize itself, as if it had switched between Landscape and Portrait. Holly hedgehog!

Back in Actionscript code, I put a listener for my main view's ResizeEvent.RESIZE and it lead me to this:

Application.stage_orientationChangingHandler()

at the bottom of the call stack. And the following comment in the implementation of stage_orientationChangingHandler:

<br />/**<br />*  @private<br />*  This is the event handler for the stage's orientationChanging event.  It<br />*  cancels the orientation animation and manually swaps the width and height<br />*  of the application to allow the application to validate itself before<br />*  the orientation change occurs.  The orientaitonChanging is only listened<br />*  for on iOS devices.<br />*/<br />

So even if we don't want to reorient ourselves, we'll be resized, should we smell any reorientation coming, just in case...

I'm really grateful that my brother and partner in crime pays attention to detail and tends to remember what he's paid attention to in crucial moments. I knew I would be kicking myself even before I had heard the question, but I let him ask it anyway:

"So, what stage do you catch that ORIENTATION_CHANGING event in?"

Darn. I should have come up with this one. Anyway, setting my listener to get the event at the capture phase and putting its priority to something high seemed to do the job. Now I was catching the event before it went to the Application and could prevent the overzealous resizing:

stage.addEventListener( StageOrientationEvent.ORIENTATION_CHANGING, orientationChanging, true, 99 );

Adobe & Apple : me = 5 : 3

Round 9: which version?

This is more of a way to mention a detail in passing, rather than a real round...

While I was messing around with the showing and dismissing of the mail composer, I noticed that there were a couple of versions of calls that do that job:

- (void)dismissModalViewControllerAnimated: (BOOL)animated;<br />- (void)presentModalViewController: (UIViewController *)modalViewController animated: (BOOL)animated;

and

- (void)presentViewController: (UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion<br />- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion

There is a warning in the Apple headers that the first set (with Modal in the name) will be deprecated. The newer versions however require iOS 5.0 or later and let you specify code to be executed when the view has finished showing or hiding, so you can better time whatever needs to be done at each event.

The comlpetion argument in the new versions calls for a code block in Objective-C and one needs to be careful what happens there, if communication with Actionscript is neccessary, as noted in this blog post. In my case an asynchronous message was all I needed, in order to notify Actionscript that the mail composer has been dismissed - have a look at the example code.

Adobe & Apple : me = I think this one should be a draw...

Final round: Workaround

So now you have it. You know why it's a workaround, rather than a solution. Let me know, when you find a more civilised one. Because I know you will. I'm sure someone who knows their way round Objective-C better will find a way to get to my CTStandaloneViewController suspect.

That's it from me. I think I'll get out of the cave now. I hear rumours it's spring out there...

Oh, no! One last thing:

Code

You can download the example code here: 

It builds with Flex 4.6 (SDK 4.6), XCode 4.3.2 and has been tested on iOS 5.1 and iOS 4.3.3.

What's in there and how to use it

  • EmailExtension/ - contains the XCode project with native code and an empty app, set up to pack up and start the AIR application, so that it can be debugged in XCode, as per this very useful tutorial: Debugging Native Extensions for AIR iOS.The app contains almost no code. What is important in it is the script, which will execute when you run it. You will need to replace YourPasswordHere with the password for your provisioning profile and set provisioning_profile and developer_identity to your profile and identity file respectivelly. To do that, in XCode select the TestEmailApp target, go to the Build Phases tab and edit the Run Script:

 in XCode select the TestEmailApp target, go to the Build Phases tab and edit the Run Script

  • certificates/ - put your .p21 and .mobileprovision files here
  • scripts/ - contains shell scripts for packaging the .ane (native extension) and .ipa (mobile application) - you'll need to edit provisioning_profile and developer_identity to what yours are called
  • FlexEmailLib/ - contains the bridge between the app and the native extension code
  • FlexApp/ - the actual app project; if you want to package it in Flex, you'll need to add the .ane to it manually in the project settings

Credits

About the Author

Radoslava is co-founder of DiaDraw. Prefers to communicate with images. Verbal communication always caused trouble with her parents. Started speaking Basic early on, followed by four years of Delphi, six years of C++, four years of ActionScript, lately converses in Objective-C. Her mum and dad hope she'll start speaking human at some point.