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.
Related
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.]
I have a UITextView that in portrait sizes 500px width.
I add it by code as subview but i can't be able in viewdidapper to get the correct size if the app is opened in landscape mode.
After the device rotation it works right. If opened landscape no.
How can i calculate the correct dimension of this textview when opening landscape?
Thank you
Add a notifier in the viewWillAppear function
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
The orientation change notifies this function
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
and you calculate the size of your textView
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
//calculate the portrait textView size
}
else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
//calculate the landscape textView size
}}
i hope this help
HI I am developing an iOS Universal application for both orientation Portrait and Landscape
I am using following code for adjusting my UI according to orientation change
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)deviceOrientationDidChange:(NSNotification *)notification
{
[self changeTheOrientation];
}
- (void)changeTheOrientation
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
if ( UIDeviceOrientationIsPortrait(orientation))
{
//For Portrait set frame size
lblBookInfo.frame = CGRectMake (0, 20, 718, 50);
btnClose.frame = CGRectMake (718, 20, 50, 50);
}
else if (UIDeviceOrientationIsLandscape(orientation))
{
//For Landscape set frame size
lblBookInfo.frame = CGRectMake (0, 20, 974, 50);
btnClose.frame = CGRectMake (974, 20, 50, 50);
}
}
else
{
if (UIDeviceOrientationIsPortrait(orientation))
{
//For Portrait set frame size
lblBookInfo.frame = CGRectMake (0, 20, 290, 30);
btnClose.frame = CGRectMake (290, 20, 30, 30);
}
else if (UIDeviceOrientationIsLandscape(orientation))
{
//For Landscape set frame size
if ( [[UIScreen mainScreen] bounds].size.height==568 ) {
} else {
}
}
}
}
The basic code structure which I am using for handling different UI frame with change in orientation is above.
But the problem which i am facing is that the UIDeviceOrientationDidChangeNotification is not fired every time , I think it gets missed for some times , I have checked this by setting proper breakpoints , this problem makes look my Ui really bad as sometimes UI do not change accordingly to the frame.
This approach works properly for some initial UI changes but get failed after some (10-20)orientation changes.
Is it the correct approach which I am using or is there any better approach to handle different orientations in iOS
I am developing app for (iOS 6 and iOS 7).
Use the following code to detect orientation in the callback for the UIDeviceOrientationDidChangeNotification -
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
Then to detect if it is landscape or portairt you can -
if (UIDeviceOrientationIsLandscape(deviceOrientation) {
//do landscape related view changes
} else if (UIDeviceOrientationIsPortrait(deviceOrientation) {
//do portrait related view changes
}
I have seen this line being added before registering for orientation change notifications. Usually in awakeFromNib method -
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
Then register for notifications -
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
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
}
}];
}
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.