When does it changes orientation to default orientation? - ios

I'm developing an iOS application with latest SDK.
My app only supports Landscape right orientation and it only has one view controller. I set the only available orientation modifying myProject-info.plist.
I'm trying to understand why this code outputs a log for a view in Portrait mode when I only supports landscape right orientation.
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
UIView *videoView = self.videoPreviewView;
NSLog(#"Video frame: %#", NSStringFromCGRect(videoView.frame));
}
Console output:
Video frame: {{0, 0}, {320, 568}}
I'm working with AVCaptureVideoPreviewLayer and I need to set its frame, but when I do it on viewWillAppear: I get portrait values but orientation value is Landscape right.
Is there an event or anything similar that i gets triggered when orientation changes to landscape?
When does the app changes orientation to default orientation?

Go into xCode, and click the project name at the top of the screen (with an xcode LOGO underneath it).
Then reveal the iPhone/Ipad development info and select the orientations you want to be available for users :)

If you add the line:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil];
then it will call the -(void)orientationChanged; method every time the orientation is changed. You can then do something like:
-(void)orientationChanged {
if (UIDeviceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) {
//device is portrait;
}
else {
//device is landscape;
}
}
Then you can use this to determine the layout of your views and setup based on the orientation.
EDIT:
You can also check if the orientation returned by the statusBar's orientation is equal to UIDeviceOrientationLandscapeLeft/LandscapeRight/Portrait/PortraitUpsideDown.

Related

Should I expect inconsistencies in screen dimensions between iPhones and iPads after orientation changes?

On iOS 9.3 iPhones and iPads (actual devices and in the simulator), I am getting inconsistent information (between the device and the simulator) about the [UIScreen mainScreen].bounds after an orientation change notification.
My view controller adds a notification for orientation changes when the view loads:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(deviceOrientationDidChangeNotification:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
The behaviour when the orientation changes is as follows:
- (void) deviceOrientationDidChangeNotification:(NSNotification *) notification {
CGRect frame = [UIScreen mainScreen].bounds;
NSLog(#"Main screen is w=%f h=%f", frame.size.width, frame.size.height);
}
Running this on an iPhone, after the orientation changes, the log message indicates a frame width and height that correspond with the post orientation change screen dimensions.
Running this on an iPad, after the orientation changes, the log message indicates a frame width and height that correspond with the pre orientation change screen dimensions.
Is this kind of inconsistency something that needs to be coded around? I could start trying to detect what the orientation is (landscape or portrait) and then using the width and height values that make sense but that is pretty hacky.
The right way to handle this is to avoid the notification system, instead using the
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator
method on the UIViewController where you want to adapt the UI for an orientation change.

UIWindow orientation issues (two UIWindows and landscape mode)

I know that this looks like a huge question but it's not so don' be afraid to read it. Mostly it's me explaining how stuff works.
I have two UIWindows in my app. The first one is the main window which gets created by the app by default. The second one is called modalWindow and is also created in the app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.modalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.modalWindow.windowLevel = UIWindowLevelStatusBar;
//Other stuff I need
//....
return YES;
}
Most of my app is portrait only but there is one view controller where the user can switch to landscape orientation. I'm listening for landscape change via:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
This works fine and in the orientationChanged method I present the landscape view controller. All is well at this point. If I rotate back to portrait I get the orientationChanged fired once more at which point I dismiss the landscape view controller.
Now let's explain how the second window comes into play. When in landscape mode there is an action (just a button press) which presents a new view controller on the second window (the modalWindow). Ok let's explain this.
The app delegate has two methods which look like so:
- (void)presentActivityOnModalWindow:(UIViewController *)viewController
{
self.modalWindow.rootViewController = viewController;
self.modalWindow.hidden = NO;
}
- (void)modalTransitionFinished:(NSNotification *)notif
{
self.modalWindow.hidden = YES;
self.modalWindow.rootViewController = nil;
}
I call the presentActivityOnModalWindow via the [[[UIApplication sharedApplication] delegate] performSelector....]. I know it isn't the best practice but it works fine. As for the dismissal I use NSNotificationCenter to post the notification about dismissal. The view controller that gets presented on the modalWindow is supporting only portrait mode. It's returning YES in shouldAutorotate and UIInterfaceOrientationMaskPortrait in supportedInterfaceOrientations.
Ok a lot of explaining but now we get to the problem. When I rotate to landscape, pop up the modalWindow, dismiss the modal window and rotate back to portrait my main window is still in landscape mode and everything looks catastrophic.
I tried adding an observer to both window and modalWindow and logging changes in frame and bounds and here are the results:
Did finish launching:
window frame {{0, 0}, {320, 568}}
window bounds {{0, 0}, {320, 568}}
modalWindow frame {{0, 0}, {320, 568}}
modalWindow bounds {{0, 0}, {320, 568}}
Rotate to landscape:
window frame {{0, 0}, {568, 320}}
Open activity in landscape:
modalWindow frame {{0, 0}, {320, 568}}
modalWindow frame {{0, 0}, {320, 568}}
Dismiss activity:
none
Rotate back to portrait:
none
So as it seems my window does not get back to normal frame when we get back to portrait mode. How to fix this? If I need to provide any more details feel free to ask.
The system will only handle rotation of your keyWindow. If you have other windows you'll have to handle rotation yourself.
I also think that modal controllers is the way to go. But if you really want to handle rotations take a look at how other "custom windows" libraries handle rotation. Alert views are a great example:
https://github.com/search?o=desc&q=uialertview&s=stars&type=Repositories&utf8=✓
Apple does not encourage using windows in this manner. It is probably best to use an UIViewController displayed modally in stead of your
self.modalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
Presenting an UIViewController modally is done from an UIViewController instance using
[self presentViewController:<#(UIViewController *)#> animated:<#(BOOL)#> completion:<#^(void)completion#>]
You are creating 2 windows and Apple recommends against this. An app should really have as many windows as there are display devices.(SEE UIWindow reference )
I would suggest using 2 views and "drawing" everything onto those views according to the orientation. Then make one view hidden, when the other is displayed.
Alternatively you could just use 1 view and make adjustments at the time of orientation changes. The ease of doing this depends on the type of information being displayed.
Orientation changes are best detected by AppDelegate code.
Here is a sample of code I use to detect an orientation change and then a check for the new dimensions of the window.
- (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation
{
//other stuff from rotate:
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect appFrame = [[UIScreen mainScreen ]applicationFrame];//using frame so status bar is not part of calculations.
if (orientation == UIInterfaceOrientationLandscapeRight
||
orientation == UIInterfaceOrientationLandscapeLeft
)
{
self.currentAppScreenWidth = appFrame.size.height;
self.currentAppScreenHeight = appFrame.size.width;
[YOURapp reArrangeSubViews_and_Layouts: appFrame];//Something like this - maybe.
}
else
{
self.currentAppScreenWidth = appFrame.size.width;
self.currentAppScreenHeight = appFrame.size.height;
[YOURapp reArrangeSubViews_and_Layouts: appFrame];//Something like this - maybe.
}
}
Hope that helps.
One other thing. If you are displaying the same info with a different layout, then you should be able to lay it out using Xcode tools, to adapt to orientation in a suitable way. If certain things are still not perfect, then try to tweak using code.
If you are displaying completely different information, the 2 view approach might be the easiest.
[Maybe some more information about the differences in the data being displayed on the 2 screens, if the above solution does not work for you.
Cheers.]

Handling rotation and adjusting views programmatically (Xcode)

Here is the problem I've been struggling with:
I'm creating a view programatically using loadView.
Once it's loaded it looks just great in Portrait view. However, I want to handle rotation of the device. Therefore I use willAnimateRotationToInterfaceOrientation method.
Within this method I call a function that adjust all the elements. What that function does is just goes through all my views and sets new CGRect to each of them. It works just fine on portrait orientations (up and upside-down), but once I change orientation to horizontal, it crops.
Two questions:
What is the most likely reason for such behavior?
How would you suggest handling device rotation without creating a separate view for horisontal / vertical orientations?
I think you are missing the key part. When you are setting the frame at portrait view at view did load, view got the frame but when it change to the landscape it change but again from there i think you are not setting the frame for portrait view. use notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
-(void) orientationChanged:(NSNotification *)notification
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if((orientation == UIDeviceOrientationPortrait) || (orientation == UIDeviceOrientationPortraitUpsideDown)) {
// set frame here
}else if ((orientation == UIDeviceOrientationLandscapeLeft) || (orientation == UIDeviceOrientationLandscapeRight)){
// set frame here too
}
}];
}

Objective-c device orientation

I am building a template app. I am having some issues with the device rotation/orientation. I have the following in my appDelegate didFinishLaunching:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
and in my VC viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
...
//add the title bar
titleBar = [[OAI_TitleBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 1024.0, 50.0)];
titleBar.titleBarTitle = #"OAI Template";
titleBar.hasAccount = YES;
titleBar.hasReset = YES;
titleBar.hasHome = YES;
[titleBar buildTitleBar];
[self.view addSubview:titleBar];
This is my deviceOrientationDidChange method:
//rotate the vc view
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
[self.view setFrame:CGRectMake(0.0, 0.0, 1024.0, 768.0)];
} else if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation)) {
[self.view setFrame:CGRectMake(0.0, 0.0, 768.0, 1024.0)];
}
//call the rotation management methods of various classes
[titleBar adjustForRotation:orientation];
}
This works fine when I rotate the device, the views adjust as expected. But when the app launches, the x,y coordinates are wrong - when I log the frame when launching in portrait orientation, the VC view's frame is {{0, 20}, {768, 1004}}, when launching in landscape mode it is {{20, 0}, {748, 1024}}
The titlebar frame is {{20, 0}, {748, 1024}} for landscape and portrait (which is as I coded it).
However, what I am seeing in the simulator and on my device is drastically different. What you should see is a black bar (50 pixels or so in height) at the top with a logo on the left, followed by some buttons and then the app title aligned right. As you can see from the images below, when the app launches in either orientation it's opening x/y coords are no where near 0/20. Any help would be appreciated.
What it looks like to me is that when the app launches in landscape it is being displayed as portrait and when it launches as portrait, though the display is right, it is off by about 20.0f or so. Any help would be appreciated.
Solved it, I
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight;
}
in my viewController, made sure the correct orientations were in my plist and it loads fine.

iPad stop screen from Rotating Xcode 4

I am building an iPad app in Xcode 4. The app is suposed to always show in Landscape view. to achieve this I have tried the following:
In the Target summary screen I selecte only Landscape Left as a Supported Device Orentation.
In the Target Info screen / Info.plist set the Supported interface orientations(iPad) to Landscape (left home button)
This leads the app the to start in landscape mode, but if I rotate the device it still changes its orientation. Also, when I have a UIViewController presented with presentationStyle UIPresentationFormSheet it rotates to portrait the moment it shows.
In some other threads / forums it was adviced to create a category for UIViewController and rewrite
-(UIDeviceOrientation)interfaceOrientation;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
To always rotate to the Device Orientation (LandscapeLeft) or specifically LandscapeLeft, also to no AutoRotate unless you rotate to LandscapeLeft.
When I set these functions like this (Or for example allow no rotation at all) the app always appears in portrait mode, and wont rotate, not even to LandscapeLeft. The only way to have the app start in Landscape mode is when I allow for rotation no matter what the interfaceOrientaton is.
Does anybody know how I can fix this?
The category I implemented:
#implementation UIViewController(Extends)
-(UIDeviceOrientation)interfaceOrientation
{
return [[UIDevice currentDevice] orientation];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
return YES;
else
return NO;
}
#end
The only place that I can find a Portrait Orientation to be defined is the original window on the MainWindow.xib, but this cannot be altered, and every thread/forum says that that particular setting is/should not be the issue.
As far as I can tell the steps you took should prevent rotation of the interface.
You can always try to override the calls that do the orientation in every viewcontroller of your app. That should at least give you a clue where the rotation is happening. After which a breakpoint can possibly tell you more.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
NSLog( #"will rotate to orientation %d in %#", interfaceOrientation, NSStringFromClass([self class])
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
NSLog( #"did rotate from orientation %d to %d in %#", fromInterfaceOrientation, [self interfaceOrientation], NSStringFromClass([self class])
}

Resources