Notification listeners causing crashes - iphone

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

Related

Callback method on auto rotation, iOS and iPad

I am working on iPhone game, i was curious to know if there is a call back function tht gets called when the device forces auto rotation on the game, so that i can change the HUD elements.
Or shd i run infinite loop that checks if the app has rotated in a different thread? problem is i dont think this is an effective way. Does anyone have any good solution for this.
All you have to do is this:
In your didfinishlaunching write this
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification object:nil];
and then copy the following callback method
- (void) didRotate:(NSNotification *)notification
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft)
{
NSLog(#"Landscape Left!");
self.isLandscapeLeft=1;
}else if(orientation == UIDeviceOrientationLandscapeRight){
NSLog(#"Landscape Right!");
self.isLandscapeLeft=0;
}
}
Likewise you can check orientation for potrait mode,face-up,face-down,upside down and landscapeleft and landscape right orientation.:)
Yes there is, as long as the device is not orientation locked you can register to listen to
UIDeviceOrientationDidChangeNotification
which can be seen here http://developer.apple.com/library/ios/#documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html
If you want to overcome the possibility of the device being orientation locked, then you have to manually monitor the accelerometer of the device.
In a game where orientation is essential, manually monitoring is recommended, since the first method has a little delay.

UIDeviceOrientationDidChangeNotification doesn't work while device is on table?

Hi there i have a problem,
In my AppDelegate i have used method beginGeneratingDeviceOrientationNotifications to start notify me when device starts rotating.
It works fine if i hand-held my ipad but when it is kept on table it doesn't work as expected.
it fires UIDeviceOrientationUnknown notification.
Also this notification gets started after UI launches not on splash screen.
following is my code:
if([[[PulseUIFactory Factory] GetUICreator] IsIPad])
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
current device stars giving proper values.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
then some where i launches my UI as
[self Launch_UI];
but notification starts responding after [self Launch_UI]; call even if notification is registered before its call...
Please any help is appreciable!!!
When you place your device on a table, [[UIDevice currentDevice] orientation] will return UIDeviceOrientationFaceUp. Then if your device remains sitting on the table face up, it doesn't matter how you rotate it on the table, the current device orientation will still be UIDeviceOrientationFaceUp.
If the device has issues determining the orientation, you may get UIDeviceOrientationUnknown. See this tutorial on how to handle device rotation using UIDeviceOrientationDidChangeNotification.
Regarding your notification only firing after the UI is loaded, the UIDeviceOrientationDidChangeNotification will only fire when the device is rotated. So if you are not rotating your device until after the UI loads, you wont get a notification. If this is not the cause of the issue, I'd have to see more of your code to have a better idea of what is going on.

iPhone - too sensitive rotation detection

I have a UIActionSheet based class. The ActionSheets created by this class are designed to vanish if a device orientation rotation is detected.
So, when this class begins I have
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
and on the didRotate: i have the dismiss, that sends the ActionSheet away.
The only little, tiny problem is this: any vibration makes the actionSheet dismiss. Even tapping with a little bit more strength will make the popoup dismiss. Even if you are typing inside the actionSheet.
I don't have any accelerometer or coremotion enabled.
Any clues?
thanks
EDIT
I have discovered that the problem is that initially the orientation is being reported as UIDeviceOrientationUnknown and the trepidation makes it report the correct orientation. As UIDeviceOrientationUnknown is different from the correct orientation, this is seen as a rotation change...
Now I am doomed.
It's difficult to solve this cleanly, because the best methods for detecting the type of rotation you're interested in are called on UIViewController rather than UIView. In UIViewController you have willRotateToInterfaceOrientation:duration:, which tells you that an autorotation is about to occur. This method is on UIViewController because the controller has the responsibility of allowing or refusing autorotation. In order to convey this information to your view, you can post an NSNotification whenever a rotation is about to begin. Then, your UIActionSheet subclass can listen for this notification and shape itself appropriately. Alternatively, you could add a method to your subclass to notify it of rotations directly. It depends on how often you need to listen for autorotation in UIView subclasses and also how strong your need for reuse is.
just check for which interfaceOrientation you're having. since iOS something, PortraitUp and facedown are supported interface orientations. What's happening to you is that portrait (when lying on a table) is switching to face-up and vice-versa. Just check for that. Please be careful, you're looking at UIDevice Orientation, which is NOT UIInterfaceOrientation!
In my case, I was doing
if(orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight)
{
//Do Stuff
}
else
{
//Do Stuff
}
So I was going inside my else on FACEUP and FACEDown event which was wrong !
if(orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight)
{
//Do Stuff
}
else if(orientation == UIDeviceOrientationPortrait)
{
//Do Stuff
}

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.

UIDevice currentDevice's "orientation" always null

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.