My question is less of how to fix the issue and more of why is this happening:
Starting in iOS 8.3, when displaying videos on a second screen (through either AirPlay or Lightning -> HDMI) the screen is rotated by 90ยบ. This isn't a problem on previous versions of iOS or when the app is launched in portrait instead of landscape.
I've created a workaround by checking for iOS version and screen rotation and then rotating the view for the second window. In case anyone else has this problem, here's my solution:
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.3f) {
CGFloat width = (_externalWindow.frame.size.width > _externalWindow.frame.size.height) ? _externalWindow.frame.size.width : _externalWindow.frame.size.height;
CGFloat height = (_externalWindow.frame.size.width < _externalWindow.frame.size.height) ? _externalWindow.frame.size.width : _externalWindow.frame.size.height;
CGRect rotatedFrame = CGRectMake(0.0f, 0.0f, width, height);
_externalWindow.frame = rotatedFrame;
if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft && [[UIDevice currentDevice].systemVersion floatValue] < 9.0f) {
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_2 * 3);
_externalWindow.transform = transform;
} else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight && [[UIDevice currentDevice].systemVersion floatValue] < 9.0f) {
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_2);
_externalWindow.transform = transform;
}
}
Edit: tested this on iOS 9 and found an interesting problem that's similar to the previous problem. The orientation was displaying correctly but the frame was still rotated so only part of the content was showing. I adjusted my solution to make sure the window frame is always oriented as widescreen.
Just so you know, I was able to solve this by only allowing portrait on the VC:
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
Related
I'm trying to detect a Today Extension's orientation, but none of the typical methods seem to work.
I've tried the following:
[[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft;
[UIDevice currentDevice]orientation] = UIDeviceOrientationLandscapeLeft;
I've even tried using CMMotionManger.
Thanks for your help.
Seems like there is no bullet-proof solution for Extensions.
Look at this answer for possible workaround - https://stackoverflow.com/a/26023538/2797141
The orientation can't be detected by any normal means. However, every time the widget opens it calls "viewDidLoad". So, in viewDidLoad, detect screen width & height thusly and determine your own orientation.
int startOrientation;
int screenWidth = self.view.frame.size.width; //Screen Width
int screenHeight = self.view.frame.size.height; // Screen Height;
if (screenWidth > screenHeight) {
startOrientation = #"landscape";
} else {
startOrientation = #"portrait";
}
I know iOS 8 now returns the proper screen dimensions for the current interface orientation. To get the device width for an orientation in iOS 7 you had to return the height if the orientation is landscape or width if the orientation is portrait, but you can always return the width in iOS 8. I have already taken that into consideration for an app I'm developing that will support iOS 7 and 8. (See code below)
However, I noticed another difference. If I call this method and pass in the orientation that it will be (obtained from willRotateToInterfaceOrientation), on iOS 7 it does return the proper width that it will be but on iOS 8 it returns the width for the old (current) orientation.
How can I get the width of the screen when I know the orientation it currently is or will be on iOS 8 and iOS 7?
While I could just swap the width and height for iOS 8, this would return an incorrect value when this function is called while the device isn't transitioning to a new orientation. I could create two different methods but I'm looking for a cleaner solution.
- (CGFloat)screenWidthForOrientation:(UIInterfaceOrientation)orientation
{
NSString *reqSysVer = #"8.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
return [UIScreen mainScreen].bounds.size.width;
}
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGFloat width = CGRectGetWidth(screenBounds);
CGFloat height = CGRectGetHeight(screenBounds);
if (UIInterfaceOrientationIsPortrait(orientation)) {
return width;
} else if (UIInterfaceOrientationIsLandscape(orientation)) {
return height;
}
return width;
}
Use cases:
iPad running iOS 7:
calling [self screenWidthForOrientation:[UIApplication sharedApplication].statusBarOrientation] in viewDidAppear returns the correct width
calling [self screenWidthForOrientation:toInterfaceOrientation] in willRotateToInterfaceOrientation:toInterfaceOrientation:duration returns the correct width
iPad running iOS 8:
calling [self screenWidthForOrientation:[UIApplication sharedApplication].statusBarOrientation] in viewDidAppear returns the correct width
calling [self screenWidthForOrientation:toInterfaceOrientation] in willRotateToInterfaceOrientation:toInterfaceOrientation:duration returns the incorrect width (what it currently is before the rotation occurs)
Here is my code to calculate correct width and height for iOS7 / iOS8 before applying constraints.
- (void) applyConstraints:(UIInterfaceOrientation)toInterfaceOrientation
{
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
CGFloat heightOfScreen;
CGFloat widthOfScreen;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
// iOS 8.0 and later code here
if ([UIApplication sharedApplication].statusBarOrientation == toInterfaceOrientation) {
heightOfScreen = screenSize.height;
widthOfScreen = screenSize.width;
} else {
heightOfScreen = screenSize.width;
widthOfScreen = screenSize.height;
}
} else {
if (UIDeviceOrientationIsLandscape(toInterfaceOrientation)) {
heightOfScreen = screenSize.width;
widthOfScreen = screenSize.height;
} else {
heightOfScreen = screenSize.height;
widthOfScreen = screenSize.width;
}
}
//Applying new constraints
...
}
It is not so beautiful but it works =)
In iOS 8, the entire nature of rotation and the coordinate system is totally changed. You should not be using any events like willRotate; they are deprecated. The entire app rotates, including the screen. There are no more rotation transforms; the whole app (screen, window, root view) just gets wider and narrower, and that's how you know something has happened (or you can register to hear about the status bar changing its orientation). If you want to know the device coordinates, independent of rotation, that is what the new screen coordinate spaces are for (fixedCoordinateSpace is the one that doesn't rotate).
I have an app that works on landscape, already in the store, and decided to add a camera for a certain new feature in my next update.
with an iPad 2 the image inside the camera appears rotated 90 degrees in clockwise direction.
Once the picture is taken it appears it's saved correct position.
Needed is that always there should be a landscape mode,while taking picture and once image is clicked and saved.But image inside the camera appears to be in Portrait view. I also checked the device orientation i.e faceUp orientation.
I have used the below code to manage the orientation but could not resolve
- (BOOL)shouldAutorotate {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
// UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIDeviceOrientationFaceUp )
{
return NO;
}
else if (orientation == UIDeviceOrientationFaceDown)
{
return NO;
}
return YES;
}
In many situation need to rotate the controller and is not working.
Right now I have the inverse of the problem: it is rotating, and I want to disable.
In that ViewController I have this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
but it is auto-rotating, because not this UIViewController is asked, but his parent in UI tree. Maybe that is the problem. The root controller must return Yes for all cases, because there are a few other UIViewControllers on the stack, which has / must have Portait / Landscape support.
I can't / don't want to touch other parts, because ... there are several reasons, for eg: the app is huge, with lot of know bugs and I don't want to make 1 and test it for 1 week, other is the deadline.
Please don't suggest it shouldn't be like this and must rewritten. I know.
How to deal with this controller to force Portait ?
Please read the bolded text too: can't force the whole app to support only Portait for 1 view controller, there are many on stack!
Try marking the app's supported Interface orientations in the properties file to only being portrait. But then of course in that function you just return YES on view controllers that you want to allow rotation. But then when you push it back in the stack the other views should be portrait.
detect the Landscape rotation and rotate to Portait:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
UIInterfaceOrientation appOrientation = [UIApplication sharedApplication].statusBarOrientation;
float width = self.view.bounds.size.width;
float height = self.view.bounds.size.height;
//NSLog(#"width %3.0f, height: %3.0f", width, height);
if((fromInterfaceOrientation == UIInterfaceOrientationPortrait || fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)){
// if is rotated from Portait:
if((appOrientation == UIInterfaceOrientationLandscapeLeft || appOrientation == UIInterfaceOrientationLandscapeRight)){
// to Landscape:
CGAffineTransform transform = self.view.transform;
transform = CGAffineTransformRotate(transform, -(M_PI / 2.0));
self.view.transform = transform;
[self.view setBounds:CGRectMake(0, 0, height, width)];
}
}
else {
// it is rotated from Landscape:
if((appOrientation == UIInterfaceOrientationPortrait || appOrientation == UIInterfaceOrientationPortraitUpsideDown)){
// to Portrait:
CGAffineTransform transform = self.view.transform;
transform = CGAffineTransformRotate(transform, +(M_PI / 2.0));
self.view.transform = transform;
[self.view setBounds:CGRectMake(0, 0, height, width)];
}
}
}
it isn't the best programming paradigm, but it does the trick.
Somebody write similar like tis to accept his answer, or write a better method, if you can!
I am trying to build an augmented reality app using the CoreMotion framework. I have tried to go off of Apple's pARk sample code project, but it only works in portrait mode. I need it to work in landscape. When switched to landscape mode the subviews in the overlay view move in the opposite directions and at the wrong rate (they either move too fast or too slow across screen)
I have read other postings that provide two solutions:
Create a reference attitude and apply the inverse of that attitude to the current attitude, as suggested in the CoreMotion Tea Pot Example.
Rotate the quaternion representation of the attitude 90 degrees
I do not think that the first will work because my augmented reality app requires that it be referenced to true north.
I also do not understand the math required to do the second.
Any suggestions on how to accomplish this complex problem I welcome.
How far are you now in your augmented reality app? I think beginning by taking a look at PARk from Apple is harsh. You need to have some advanced mathematical understanding. But if you do, why not!
you can take a look at this repository, this an augmented reality project working on Portrait and Landscape mode. Here is how the rotation is handled:
- (void)deviceOrientationDidChange:(NSNotification *)notification {
prevHeading = HEADING_NOT_SET;
[self currentDeviceOrientation];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
// Later we may handle the Orientation of Faceup to show a Map. For now let's ignore it.
if (orientation != UIDeviceOrientationUnknown && orientation != UIDeviceOrientationFaceUp && orientation != UIDeviceOrientationFaceDown) {
CGAffineTransform transform = CGAffineTransformMakeRotation(degreesToRadian(0));
CGRect bounds = [[UIScreen mainScreen] bounds];
switch (orientation) {
case UIDeviceOrientationLandscapeLeft:
transform = CGAffineTransformMakeRotation(degreesToRadian(90));
bounds.size.width = [[UIScreen mainScreen] bounds].size.height;
bounds.size.height = [[UIScreen mainScreen] bounds].size.width;
break;
case UIDeviceOrientationLandscapeRight:
transform = CGAffineTransformMakeRotation(degreesToRadian(-90));
bounds.size.width = [[UIScreen mainScreen] bounds].size.height;
bounds.size.height = [[UIScreen mainScreen] bounds].size.width;
break;
case UIDeviceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(degreesToRadian(180));
break;
default:
break;
}
[displayView setTransform:CGAffineTransformIdentity];
[displayView setTransform: transform];
[displayView setBounds:bounds];
degreeRange = [self displayView].bounds.size.width / ADJUST_BY;
}
}
You have to rotate all your annotations and then your overlay view after the device rotation!
If you're really stuck, and are willing to use another toolkit, try iPhone-AR-Toolkit. It works in both portrait and landscape.