UIDevice currentDevice's "orientation" always null - iphone

As per the title.
Calling [[UIDevice currentDevice] BeginGeneratingDeviceOrientationNotifications] has no effect.
DidRotateToInterfaceOrientation etc events are working fine, but I need to be able to poll the device orientation arbitrarily.
How can I fix/do this?
The long story:
I have a tab application with a navigation controller on each tab. The root view of tab number one is a graph that goes full screen when the orientation changes to landscape; however this needs to be checked whenever the view appears as the orientation change could have occurred elsewhere, so I was hoping to poll the orientation state whenever this view appears.

UIDevice's notion of orientation seems to be only available on actual devices. The simulator seems to always return 0 here, regardless of whether the notifications have been enabled as the docs suggest. Irritatingly inconvenient, but there you go.
I find this works fine on the actual device:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
NSLog(#"orientation: %d", [[UIDevice currentDevice] orientation]);
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

seems like a silly question, but isn't it
beginGeneratingDeviceOrientationNotifications
( lower case b )
...

If you check [[UIDevice currentDevice] orientation] in - (void)viewDidLoad you will always get nil.
Check it in *- (void) viewDidAppear:(BOOL)*animated method and you

this is as per iMeMyself said in the comments above - this samed me a lot of time and I think is the right answer so I wanted to highlight it here:
UIDeviceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
//do stuff here
}
else if (UIInterfaceOrientationIsPortrait(interfaceOrientation))
{
//or do stuff here
}

Wouldn't [[UIDevice currentDevice] orientation] give you the current orientation state? You could check for this in your viewWillAppear method of the view controller that wants to poll.
Edit: Other than that, there are various ways to get the current orientation, such as using the statusBarOrientation property in UIApplication, or interfaceOrientation property in UIViewcontroller.

Related

Notification listeners causing crashes

I'm using the following code to detect/listen for when the iPad changes device orientation.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification"
object:nil];
This calls my didRotate: method when something changes. In my didRotate: method, I use UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; to figure out what the orientation is, and apply my code accordingly.
However, in real life testing, I noticed that didRotate: gets called every second if the iPad is in a person's hand. It appears the listener is literally listening for every little tilt and shift in the iPad, which obviously happens a lot in a person's hands (as opposed to flat on a desk).
Any ideas on how I can fix this? I could change my code to use interface orientations, but I've been having trouble with it for whatever reason. Thank you.
*UPDATE: This listener is created in my UIImageView subclass. There are about a dozen or more on the screen. I have added [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; to my removal methods for when I remove an instance. That helps explain why didRotate kept showing up so much (my error).
However, I have narrowed down crashes: whenever I remove this an instance of this subclass, and rotate my iPad, I crash. Sometimes I get weird messages like [__NSArrayM didRotate]: is unrecognized selector (and other wierd objects like UIPanVelocity...something). Is my listener still listening after the instance is removed?
FIXED: Thank you for your help. I finally noticed what was wrong. I was removing the instance without removing the observer and ending notifications. Adding following code into my instance removal methods fixes my problem:
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] removeObserver:self];
I'm guessing that you add observers repeatedly and never remove them. This means that the same entry point may get invoked multiple times for a single event. It also means that when the "self" object goes away you get a crash.
The notification is only sent if the device changes from one orientation to another orientation - it doesn't get fired more times than necessary.
UIDeviceOrientation has 2 more orientations than UIInterfaceOrientation. It has FaceUp and FaceDown. Possibly it is these that are getting triggered, and you're not interested in them.
typedef enum {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait,
UIDeviceOrientationPortraitUpsideDown,
UIDeviceOrientationLandscapeLeft,
UIDeviceOrientationLandscapeRight,
UIDeviceOrientationFaceUp,
UIDeviceOrientationFaceDown
} UIDeviceOrientation;
Maybe you would prefer to listen for UIInterfaceOrientation changes in the view controller that is currently onscreen.
typedef enum {
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;
.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

iPad launch orientation when flat on surface

I have the following code:
-(void)viewWillAppear:(BOOL)animated {
UIDeviceOrientation orientation = [[UIDevice currentDevice] UIInterface];
if(orientation==UIDeviceOrientationLandscapeLeft || orientation==UIDeviceOrientationLandscapeRight) {
NSLog(#"Device Landscape");
} else {
NSLog(#"Device Portrait");
}
}
This works perfectly for the simulator and on the device but only if the iPad is not flat on a surface, for example if i lay the iPad on my desk and load the view it will say its portrait and so I cant detect the orientation when the view appears.
Any ideas of the best way to detect the interface orientation when the viewWillAppear is called?
Thanks
You should use [self interfaceOrientation] instead of the device orientation.
[[UIApplication sharedApplication] statusBarOrientation] also works if you are somewhere in the code that does not have access to [self interfaceOrientation]
First, do you know about the interfaceOrientation property of UIViewController? It looks like you are trying to duplicate it. If you really want to do that, simply subscribe to device orientation change notifications, check if the new orientation is valid interface orientation (the is a macro for that) and if yes, store the value to some variable (like lastSeenOrientation). Then you can rely on that value wherever you want.

Inaccurate device orientation returned by UIDeviceOrientationDidChangeNotification

I'm listening to the UIDeviceOrientationDidChangeNotification in order to adjust my UI based on the device's orientation.
The problem is that the device orientation I'm getting from the notification seems inaccurate. If I start with the phone in portrait orientation and vertical (as if taking a picture with it) the orientation I get from the notification is correct. As the tilt of the phone approaches horizontal (as in laying flat on a table top) the orientation switches to landscape. This happens much before the phone is actually flat on the table. And this is without rotating the phone towards landscape at all. Is as if it had a preference for landscape.
When using other apps, like mail, I don't see this same behavior. It seems that mail only switches orientation once it's really sure you've gone to the new orientation.
Any help greatly appreciated.
Thanks,
I found my problem.
In viewWillAppear I have:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(didChangeOrientation:) name: UIDeviceOrientationDidChangeNotification object:
[UIDevice currentDevice]];
And here is the notification handler:
- (void) didChangeOrientation: (id)object {
UIInterfaceOrientation interfaceOrientation = [[object object] orientation];
//DLog(#"val is %i", interfaceOrientation);
if (interfaceOrientation == UIDeviceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationLandscapeRight || interfaceOrientation == UIDeviceOrientationPortrait) {
[self orientInterface:interfaceOrientation];
}
By checking that the orientation is one of the two landscapes or the two portraits I'm ignoring the UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown orientations. That way setting the phone in one of this two orientations won't have an effect on my interface's orientation.
My problem was that I was no considering the faceUp and faceDown and was handling them both in an else statement which was assuming landscape.
If your only interest is your interface orientation (ie landscape or portrait orientation of the device), you should NOT use UIDevice:orientation (or the UIDeviceOrientation* constants if you will), but rather use the UIApplication:statusBarOrientation, which uses the UIInterfaceOrientation* constants.
I use the following code to check for landscape modus:
static inline bool isOrientationIsLandscape() {
return UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]);
}
And for portrait modus:
static inline bool isOrientationIsPortrait() {
return UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]);
}
This cost me a whole morning to figure out, because there is no UIDeviceOrientationFace[Up|Down] on the simulator, only on the real device. So my app worked on the simulator all the time, but on the actual device I had some undefined behavoir during testing every now and then.
Now it works on the actual device like it does on the simulator.
HTH
The notifications are just one way to get at it, you can also read out the accelerometer yourself and implement it in exactly the way you see fit (with a delay and a certain time of non-rotation for example).
Don't know if it's a power drain to get those readouts, but if so, use the notification to know when things are moving, and then fire up the accelometer-reading.

How can I programmatically know which interface orientaton I am in when the application loads?

Using some of the methods, I am able to check the orientations to which I am rotating my device. But how can I know the orientation in which I am in when the app is loaded? I want to write the code to rearrange the views in the viewDidLoad method by checking the way my orientation is in?
UIDeviceOrientation myOrientation = [[UIDevice currentDevice] orientation];
You could also use something similar to below in the ViewDidLoad/ViewWillAppear methods.
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){
}

iPhone SDK: Forcing landscape mode for a single part of an app

Yet another landscape mode question.
We've all seen the
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
Nonsense. I have not been able to get this to work on the simulator or on the device.
What I get is this:
http://i47.tinypic.com/zl9egh.png
when I really want this:
http://i45.tinypic.com/xms6cm.png
As you might be able to tell, it's flipping the phone but it's not realizing it has been flipped.
I've seen the hidden method
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
But I need this app to be on the app store, and I don't know if they will crack down on that.
Does anyone have any idea on how to get this damn thing to straighten itself out?
Ideally your view controller should implement:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
Then the only allowed orientation for that view controller is one of the landscape orientations.
Unless you have a pretty complex view hierarchy that utilizes a tab bar controller, this should cause the view to rotate when it's pushed.
If it doesn't work you can try:
[[UIDevice currentDevice] performSelector:
#selector(setOrientation:)
withObject:(id)UIInterfaceOrientationLandscapeRight];
I have an app in the AppStore that uses this code because in my case I do have a complex hierarchy of views that keeps the one I need to be landscape only from working.
If you want to rotate a single part of the app you can use the CGAffineTransform transform property of a UIView. For an example check this thread:
How do I apply a perspective transform to a UIView?