No perfect way to detect device orientation on iPad? - iphone

EDIT: The given answer works on the device, but beware it fails on the simulator.
When my iPad starts up, I show a loading label, centered in the middle of the screen. I set its autoresizingMask so it recenters on orientation change.
As the app starts up, the label's text changes, so I want to recenter the label based on its new length. However, the following piece of code doesn't center the label correctly:
- (void) setLabelText:(NSString*)text {
CGSize maximumLabelSize = CGSizeMake(500,20);
CGSize expectedLabelSize = [text sizeWithFont:loadingLabel.font
constrainedToSize:maximumLabelSize
lineBreakMode:loadingLabel.lineBreakMode];
loadingLabel.frame = CGRectMake(self.view.frame.size.width/2-expectedLabelSize.width/2,
loadingLabel.frame.origin.y,
expectedLabelSize.width,
loadingLabel.frame.size.height);
loadingLabel.text = text;
}
I also considered checking [[UIDevice currentDevice]orientation], and if the iPad is in landscape mode, then I'd use self.view.frame.size.height to set the xOrigin of the label.
However, if the device is face up or face down, (and not landscape or portrait) then this method fails. I also have a lastOrientation variable in my appDelegate, which remembers if the app is in landscape or portrait, even when face up or face down, based on the device's last known orientation. However, at start-up, this variable isn't necessarily set.
Is there some simple solution I am missing here, so I can resize and center my label?
EDIT: I tried checking UIStatusBarOrientation based on the advice posted, but it doesn't work:
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft
|| [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
NSLog(#"landscape");
width = self.view.frame.size.height;
} else {
NSLog(#"portrait");
}
This always logs portrait, at least on start-up, on the simulator.

Check [[UIApplication sharedApplication] statusBarOrientation]

I found a trick to solve the FaceUp orientation issue!!!
Delay the orientation check till AFTER the app has started running, then set variables, view sizes, etc.!!!
//CODE
- (void)viewDidLoad {
[super viewDidLoad];
//DELAY
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(delayedCheck)
userInfo:nil
repeats:NO];
}
-(void)delayedCheck{
//DETERMINE ORIENTATION
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait ){
FACING = #"PU";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown ){
FACING = #"PD";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft ){
FACING = #"LL";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight ){
FACING = #"LR";
}
//DETERMINE ORIENTATION
//START
[self setStuff];
//START
}
-(void)setStuff{
if( FACING == #"PU" ){
//logic for Portrait
}
else
if( FACING == #"PD" ){
//logic for PortraitUpsideDown
}
else{
if( FACING == #"LL"){
//logic for LandscapeLeft
}
else
if( FACING == #"LR" ){
//logic for LandscapeRight
}
}
//CODE
You can addSubviews, position elements, etc. in the 'setStuff' function ... anything that would initially depend on the orientation!!!
:D
-Chris Allinson

Related

iOS6 Device Rotation To Be Restricted

For my app, I want to let the device rotate anyway but upside-down. This is working fine. However, I want to stop the app from rotating specifically from
landscape left -> landscape right - and vice versa
If anyone is curious, this is because that rotation messes up my layouts, as they each rotate from a common point
My code for iOS 5, which I think would work, is like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
NSLog(#"Rotating");
if((lastOrient == 3 && toInterfaceOrientation == 4) || (lastOrient == 4 && toInterfaceOrientation == 3)){
lastOrient = toInterfaceOrientation;
return NO;
}
lastOrient = toInterfaceOrientation;
return YES;
}
Where 3= landscape left and 4= landscape right
Any suggestions on how to do this with iOS6? Or a completely different solution?
shouldAutorotateToInterfaceOrientation is deprecated in ios6. Use this:
- (BOOL)shouldAutorotate {
UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];
if (lastOrientation==UIInterfaceOrientationPortrait && orientation == UIInterfaceOrientationPortrait) {
return NO;
}
return YES;
}
Haven't tested this code. You can get more info on these posts:
shouldAutorotateToInterfaceOrientation is not working in iOS 6
shouldAutorotateToInterfaceOrientation not being called in iOS 6
Ok I answered my own question here:
The good news is, there is definitely a way to do this! So heres the basics:
In iOS6, it is up to the appDelegate to handle whether the app can rotate in general. Then, when the device gets a rotation signal, it will ask your view for its supported orientations. This is where I implemented my code. In fact, shouldAutorotate() plays no role in the solution.
So I create a variable to keep track of the last orientation, and change it in
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
This way I can compare orientations
-(NSUInteger)supportedInterfaceOrientations
{
NSLog(#"Last Orient = %d", lastOrient);
NSUInteger orientations = UIInterfaceOrientationMaskPortrait;
if (lastOrient != 3 && lastOrient != 4) {
NSLog(#"All good, rotate anywhere");
return UIInterfaceOrientationMaskAllButUpsideDown;
}
else if(lastOrient == 3){
orientations |= UIInterfaceOrientationMaskLandscapeRight;
NSLog(#"Can only rotate right");
}
else if(lastOrient == 4){
orientations |= UIInterfaceOrientationMaskLandscapeLeft;
NSLog(#"Can only rotate left");
}
return orientations;
}
Seems to work for me. Slightly a hack, but it does what it needs to do

Hide UIView on device rotation - doesn't work when device is horizontal

I'm trying to hide an image in a view controller when the device is rotated. I'm posting a notification in PlayerViewController and am listening for it in the app delegate, which is responsible for the bannerView:
- (void)orientationChanged:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if ((orientation == UIDeviceOrientationLandscapeLeft) ||
(orientation == UIDeviceOrientationLandscapeRight)) {
bannerView.hidden = ([[self.navigationController visibleViewController] isKindOfClass:[PlayerViewController class]]) ? YES : NO;
} else {
bannerView.hidden = NO;
}
}
The PlayerViewController sends a notification and the app delegate hides the bannerView. However, when the device is laid flat on a table, the image shows. Works fine when the device is held vertically but horizontally the image appears... odd.
Here is the code to send the notification:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
... hide other stuff in this view controller
}
Any ideas why this odd behavior is occurring?
Just one tidbit more information. In the simulator the image shows when the device is in upside-down orientation, even though I have:
- (BOOL)shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationPortrait) {
return YES;
} else {
return NO;
}
}
Your error might be happening because of when you're posting the notification.
willAnimateRotationToInterfaceOrientation is called before the orientation change takes place (hence the "will" in the method name). So if we're going from portrait to landscape, the current orientation may still be reported as portrait (it may not, it depends).
Now, the willAnimate... call returns the toInterfaceOrientation - the orientation that is going to happen.
You trigger your notification when you receive the willAnimate... call, and inside that notification call [[UIDevice currentDevice]orientation]: which will return portrait. Instead of requesting the orientation in your notification method you should instead pass the orientation provided in the willAnimate call.
If that wasn't clear, the one sentence summary: willAnimateRotationToInterfaceOrientation is called before the rotation changes.

UIViewController and Orientation am I heading in the right direction?

I have spent the last several hours trying to get this to work and I can't so I need to know if what I am trying to do is the correct thing. Its driving me mad!
My goal is to have a ViewController that detects orientation change. When it's portrait it shows a view with a UITableView, when its landscape it shows a UIView with content that I will programmatically create.
In my parent viewcontroller I have:
- (void)viewDidLoad
{
[super viewDidLoad];
TableDataViewController *tableDataController = [[TableDataViewController alloc]
initWithNibName:#"TableDataViewController"
bundle:nil];
self.tableDataViewController = tableDataController;
[self.view insertSubview: tableDataController.view atIndex:0];
[tableDataController release];
}
This loads my view containing the table view and the controller that goes with it. The user then rotates the device and the function:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toOrientation
duration:(NSTimeInterval)duration
{
if (toOrientation == UIInterfaceOrientationPortrait) {
NSLog(#"PerformAnalysisViewController: Gone to Potrait");
}
if ((toOrientation == UIInterfaceOrientationLandscapeLeft) ||
(toOrientation == UIInterfaceOrientationLandscapeRight)) {
NSLog(#"PerformAnalysisViewController: Gone to Landscape");
// Load new view here
CGRect frame = [[UIScreen mainScreen] applicationFrame];
UIView *horizView = [[[HorizView alloc]
initWithFrame:frame] autorelease];
[self setView:horizView];;
}
}
will trigger depending. However it doesn't trigger? Is this because control has passed to the Subview I have inserted in the viewDidLoad? If so how do I get it back? Do I have to get it to detect orientation and then remove itself from the superview?
If that was then working would the new view be added as I have it above? I have tried reading the Apple documentation but I can't make this work.
All help greatly appreciated.
Mike
I am using the willRotateToInterfaceOrientation method in order to handle UI rotation:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
if( interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ) {
self.backgroundImageView.image = [UIImage imageNamed:#"vertical.png"];
}
else {
self.backgroundImageView.image = [UIImage imageNamed:#"horizontal.png"];
}
}
Please check, that your Info.plist supports more than one orientation and that you have implemented shouldAutorotateToInterfaceOrientation:interfaceOrientation by returning YES.

How can I query the current orientation of an iPhone/iPad screen?

I was curious to know if there is some way to determine the orientation of a screen on iPhone/iPad.
Currently I find myself setting a member variable when this message is called:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
self.orientation = interfaceOrientation;
return YES;
}
... which feels like something I should be able to query as a state instead of having to track it when it changes.
UIViewController has the interfaceOrientation property.
Don't get confused by orientation in UIDevice, which is not the orientation of the interface, but the physical orientation.
Use this if you have a status bar on top.
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
// portrait
} else {
// landscape
}
You can do [[UIDevice currentDevice] orientation].

My app crashes after multiple rotates

My app is crashing when I try and rotate it more than a couple of times. I first thought it was just the iPhone Simulator, so I loaded the app onto an iPod touch, and it crashed after fewer rotates in a row. I suspect it's a memory leak in one of my rotate methods. The only place I can think that the crash is being caused is in willRotateToInterfaceOrientation:duration:. The only two methods related to rotate that I've added/extended are shouldAutorotateToInterfaceOrientation: and willRotateToInterfaceOrientation:duration and I don't think it's the first because it only contains the two words: return YES;. Here is my willRotateToInterfaceOrientation:duration: method so you can review it and see where the possible memory leak is.
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
UIFont *theFont;
if ((orientation == UIInterfaceOrientationLandscapeLeft) || (orientation == UIInterfaceOrientationLandscapeRight))
{
theFont = [yearByYear.font fontWithSize:16.0];
yearByYear.font = theFont;
[theview setContentSize:CGSizeMake(460.0f, 635.0f)];
}
else
{
theFont = [yearByYear.font fontWithSize:10.0];
yearByYear.font = theFont;
[theview setContentSize:CGSizeMake(300.0f, 460.0f)];
}
[theFont release];
}
yearByYear is a UITextView and theview is a UIScrollView.
You shouldn't be releasing theFont. You don't own the object.
You can also simplify what you're doing to:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if ((orientation == UIInterfaceOrientationLandscapeLeft) || (orientation == UIInterfaceOrientationLandscapeRight)) {
yearByYear.font = [yearByYear.font fontWithSize:16.0]
[theview setContentSize:CGSizeMake(460.0f, 635.0f)];
}
else
{
yearByYear.font = [yearByYear.font fontWithSize:10.0]
[theview setContentSize:CGSizeMake(300.0f, 460.0f)];
}
}
Getting rid of theFont completely.