views:

6615

answers:

5

In my program I'm moving things based on rotation, but I'm not rotating the entire view. I'm Using :

  static UIDeviceOrientation previousOrientation = UIDeviceOrientationPortrait;

 - (void)applicationDidFinishLaunching:(UIApplication *)application {   
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
    [[NSNotificationCenter defaultCenter] addObserver:self
           selector:@selector(didRotate:)
           name:@"UIDeviceOrientationDidChangeNotification" object:nil];

}

- (void) didRotate:(NSNotification *)notification{  
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

    [Self doRotationStuff:orientation: previousOrientation];
previousOrientation = orientation;

}

This works as long as, when the program is launched, the device orientation is portrait, but not if the initial orientation is landscape or upside down, because [self doRotationStuff] makes changes relative to the difference from the previous orientation.

Is there a way to detect the orientation either at launch, or right before the device is rotated?

+1  A: 

Updated:

So, from the comment discussion, it appears that you can't rely on [UIDevice currentDevice].orientation until the orientation actually changes for the first time. If so, you could probably hack it by getting raw accelerometer readings.

#define kUpdateFrequency 30  // Hz
#define kUpdateCount 15 // So we init after half a second
#define kFilteringFactor (1.0f / kUpdateCount)

- (void)applicationDidFinishLaunching:(UIApplication *)app
{
    [UIAccelerometer sharedAccelerometer].updateInterval = (1.0 / kUpdateFrequency);
    [UIAccelerometer sharedAccelerometer].delegate = self;
    accelerometerCounter = 0;
    ...
}

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)accel
{
    // Average out the first kUpdateCount readings
    // acceleration_[xyz] are ivars typed float
    acceleration_x = (float)accel.x * kFilteringFactor + acceleration_x * (1.0f - kFilteringFactor);
    acceleration_y = (float)accel.y * kFilteringFactor + acceleration_y * (1.0f - kFilteringFactor);
    acceleration_z = (float)accel.z * kFilteringFactor + acceleration_z * (1.0f - kFilteringFactor);

    accelerometerCounter++;
    if (accelerometerCounter == kUpdateCount)
    {
        [self initOrientation];
        [UIAccelerometer sharedAccelerometer].delegate = nil;
    }
}

- (void)initOrientation
{
    // Figure out orientation from acceleration_[xyz] and set up your UI...
}


Original response:

Does [UIDevice currentDevice].orientation return the correct orientation during applicationDidFinishLaunching:? If so, you can set up your initial UI according to that orientation.

If that property doesn't get set until some later time, you might try experimenting with performSelector:afterDelay: to initialize the UI after a small delay.

This code sample is from Kendall's answer below, added here for completeness:

[self performSelector:@selector(getOriented) withObject:nil afterDelay:0.0f];

I'm not sure if a zero-second delay is sufficient -- this means the code for getOriented will run during the first pass through the event run loop. You may need to wait longer for the accelerometer readings to register on UIDevice.

Daniel Dickison
During applicationDidFinishLaunching, [UIDevice currentDevice].orientation is UIDeviceOrientationUnknown
I'm clueless about how I would use performSelector:afterDelay. Can you give a code example?
I tried what you said but even [self performSelector:@selector(getOriented) withObject:nil afterDelay:10.0f]; did not have any effect
What do you mean by "did not have any effect"? Do you mean it's still `UIDeviceOrientationUnknown` after the delay? You may need to call `[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]` during launch to make sure the orientation is getting updated.
Daniel Dickison
Yes, I mean it's still UIDeviceOrientationUnknown after the delay. [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications] is being called in- (void)applicationDidFinishLaunching:(UIApplication *)application {Are you suggesting I need to activate it earlier?
Sounds like maybe it's just not possible to rely on UIDevice's orientation property until the orientation actually changes. This would be a bummer (you might want to file a bug/enhancement request with Apple). In the meanwhile, you can register for raw accelerometer notifications and hack it that way (maybe wait for 10 quick updates and base your initial orientation on that).
Daniel Dickison
Thanks Daniel, that seems to have done it
A: 

In response to your comment, I thought I could better put code here than in a comment (though really Daniel deserves credit here):

in applicationDidFinishLaunching:

[self performSelector:@selector(getOriented) withObject:nil afterDelay:0.0f];

Then you just need the method to call:

- (void) getOriented 
{ 
   UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
   // save orientation somewhere
}
Kendall Helmstetter Gelner
I tried what you said but even [self performSelector:@selector(getOriented) withObject:nil afterDelay:10.0f]; Did not have any effect
What was the orientation when it was called ten seconds later? It seems like after ten seconds the orientation should be set... or at that time, simply ask the main view controller what its orientation is.
Kendall Helmstetter Gelner
it was UIDeviceOrientationUnknown
A: 

Mort, these answers seem somewhat of a red herring; I can't see why you can't use the following built-in method for a UIViewController class:

-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {}

This method gets called automatically after a rotation has occurred (rather than with shouldAutorotateToInterfaceOrientation which only tells you it's about to happen). Handily, the variable 'fromInterfaceOrientation' contains the previous orientation. As the documentation also says, you can assume that the interfaceOrientation property of the view has already been set to the new orientation, so you then have one method with access to the old orientation and the new!

If I've missed something and you've already dismissed being able to use this method, my apologies! It just seems odd that you're creating and storing a variable for the "previous orientation" when it's provided for free in the above method.

Hope that helps!

h4xxr
That would work if I was planning on rotating the interface, which I do not want to do.
A: 

A more complete example on how to obtain device orientation from accelerator readings can be found here As the solution relies on accelerator readings, it wouldn't work on the simulator, so you'll have to work on the device... still looking myself for a solution that works on the simulator.

luvieere
+1  A: 

Depending on your circumstances, a simpler option may be the interfaceOrientation property of the UIViewController class. This is correct before a rotation.

delany