Fixing Orientation Issues with OpenFrameworks and iOS

mss_rotated_screen

Fixing Orientation Issues with OpenFrameworks and iOS

I’m using openFrameworks alongside standard UIKit elements to build my app “Make Space Ship”, where you draw cool things with triangles. I’m getting close to release, and I was having all sorts of problems getting the orientation to launch correctly.

The Problem

The app is meant to be used in landscape orientations, so I set:

ofxiPhoneSetOrientation(OF_ORIENTATION_90_RIGHT);

As I’d seen in the openFrameworks examples, and then handled orientation in testApp using deviceOrientationChanged. However, this was causing two different issues when the app was launched in something other than Landscape Right.

When the App launches, I’ve got a Space Ship (the character in the middle of the pictures below) as well as Start and Tutorial buttons. These are made with UIKit in an .xib file called TitleScreen, and overlaid on the live starfield, which is the GLView created by openFrameworks. This was accomplished by adding the TitleScreen to the GLView, like this:

titleScreen = [[TitleScreen alloc] initWithNibName:@"MakeSpaceShip" bundle:nil];

// now move the view to the GLView to allow touches, removed from window automatically
[ofxiPhoneGetGLView() addSubview:titleScreen.view];

Under some circumstances, I’d get the correct TitleScreen orientation, but the starfield would be set up with a portrait orientation, like in this picture:

You can see that the stars do not fill up the full screen – their placement is random within the current screen bounds (using ofGetWidth and ofGetHeight), so they were being initialized at 768×1024 instead of 1024×768. This fixed itself once they started moving around, but I found that unacceptable.

Another circumstance would find the GLView set up correctly, but the TitleScreen would be rotated 90 degrees out of place after it was added to the GLView, like this:

Terrible! I found Damian’s blog post about orientation and tried his method. It worked, but whenever I touched a part of my TitleScreen that was transparent, the touches would not make it through to the openFrameworks view below. These touches worked fine in the original method, so I knew there had to be another way around.

The Solution

I took some of what worked from Damian’s post, and combined it with the method that allowed touches to get through. Here’s how it goes:

There are three classes at work here.

  • TitleScreen is a UIViewController which contains the UIView for all my UIKit elements, which are present as GUI throughout the app.
  • TestApp is the openFrameworks application.
  • AppSettings is a C++ file which is injected into all my Manager classes (I’ve got a Manager for the Starfield, the Triangles, the Tutorial, etc.) and holds Application State Settings. It also acts as the communication channel between my TitleScreen and my app. It’s instantiated in the TestApp, and then I inject a pointer to it into every Manager Class when I create them.

I don’t call ofxIphoneSetOrientation in my TestApp setup method at all. here’s what I do:

   // create and initialize titleScreen
    titleScreen = [[TitleScreen alloc] initWithNibName:@"MakeSpaceShip" bundle:nil];

   // inject settings
    titleScreen.settings = settings;
    
    // adding the titlescreen to the main window sets the orientation properly
    [ofxiPhoneGetUIWindow() addSubview:titleScreen.view];

    // now move the view to the GLView to allow touches, removed from window automatically
    [ofxiPhoneGetGLView() addSubview:titleScreen.view];

Previously, I had been adding the TitleScreen to the GLView, which may or may not have already been rotated, and calls to all three methods of getting device orientation gave unpredictable results. Now, I’m adding the TitleScreen directly to the UIWindow at the top of the View hierarchy, so it orients itself correctly. However, leaving it here will cancel touch events going through to the oF app, so once it’s been oriented, I add it to the GLView, which automatically removes it from the Window, since UIViews can only have one SuperView.

There’s just one further piece missing after this, and it’s a method to rotate the GLView. openFrameworks provides a convenience method for this in TestApp called deviceOrientationChanged. However, I couldn’t get this to work right any way I tried. So instead, I added this to the TitleScreen implementation:

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    settings->setOrientation(toInterfaceOrientation);
    // snap to new rotation
    [UIView setAnimationsEnabled:NO];
}

So once the TitleScreen decides that it’s ready to move to a new orientation, it tells the AppSettings class (via a pointer called settings) to run the setOrientation method using the new orientation. Here’s what that method looks like:

    void setOrientation(int newOrientation) {
        switch (newOrientation) {
            case OF_ORIENTATION_90_LEFT:
                ofxiPhoneSetOrientation(OF_ORIENTATION_90_RIGHT);
                break;
                
            case OF_ORIENTATION_90_RIGHT:
                ofxiPhoneSetOrientation(OF_ORIENTATION_90_LEFT);
                break;
        }
    }

I’m using the ofxIphoneSetOrientation method, and flipping left and right since they seem to report backwards to what I expect. And with that, my app launches with correct orientation no matter which way the iPad is facing (Portrait, Upside-Down, Landscape-Left, Landscape-Right, sitting flat on the desk in Upside-Down mode, etc.), and correctly snaps to the right orientation if you flip it around. The final correct intro screen:

Woohoo!

I’ve learned a LOT through making this Application, so I’ll be documenting my experience here as time allows. I’ve got a new theme in the wings, too (this one dates back several years), and this blog will eventually be merged into the main mmmlabs.com site, which was not easily accomplished with the current theme there.

Hope this helps someone out!

2 comments

  1. Posted by Dorald, at Reply

    Thanks for sharing this !
    I want to se how to mix OF and UIKit Views on iOS. Can you post a example project with UIViews on OF app please ??
    I have an idea to create a game with OF and would be thankful if you show me an example project with similar of yours where i see how uiviews,testapp,nib's and touches works together . Thank you .

    • Posted by momothemonster, at Reply

      Dorald – the newest version of OpenFrameworks comes with an example that demonstrates this – in examples/ios/iosNativeExample.

Post your comment