I wanted my UIView to animate to a new position when the keyboard was shown, so used the UIKeyboardWillShowNotification and UIKeyboardWillChangeFrameNotification. The problem is that when the device is rotated without the keyboard, the view has autoresizing and rotates as it should - it looks perfect.
Unfortunately, with the keyboard displayed, rotating the device sends those notifications and thus performing a UIView animation in response gives it an odd animation. It could best be described as looking like it jumps into a new position and is then anchored round by a corner to the new orientation. Perhaps you know what I'm talking about.
Is there any way for me to detect when the device is rotating or otherwise deal with the problem when rotating when the keyboard is being shown?
For orientation-change detection use the UIDeviceOrientationDidChangeNotification.
I guess the following code will help you..
#property(nonatomic,strong)BOOL keyBoardShow;
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
[super viewDidLoad];
}
-(void)keyboardWillBeShown:(NSNotification *)aNotification {
keyBoardShow = YES;
}
-(void)keyboardWillBeHidden:(NSNotification *)aNotification {
keyBoardShow = NO;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (keyBoardShow) {
// Do the needful when the keyboard is shown
}else{
}
}
Just set bool value in keyboardWillBeShown and keyboardWillBeHidden delegate methods. And do your uiview position in willRotateToInterfaceOrientation.
Instead of relying on the built-in notifications, why don't you instead rely on what's triggering the keyboard showing in the first place? I'm guessing the keyboard pops up when a particular UITextField becomes the first responder. You should be able to use that.
Register a delegate for that UITextField and implement -textFieldDidBeginEditing:. The delegate should be a view controller. Then, to keep things loosely coupled, post your own notification from the delegate and have your view registered for that.
It's a bit more work, but it gives you much better control.
- (void)textFieldDidBeginEditing:(UITextField *)textField
Apple documentation: UITextFieldDelegate Protocol
I am currently working on an app where I would like to transition from one screen to another when the user rotates the device to landscape view. I have this working, but the screen that is being transitioned out rotates to landscape view just as the next view is coming in. A common solution to preventing the current view from rotating is to return NO for the shouldAutoRotate method. However, I need this enabled in order to transition to the next screen. I've also toyed with [UIView setAnimationsEnabled:NO] in the willRotateToInterfaceOrientation but this only hides the animation, and still rotates the current view to landscape. Here are all related rotate methods:
-(BOOL)shouldAutorotate
{
return YES;
}
//Temporarily disable rotation animation
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[UIView setAnimationsEnabled:NO];
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[UIView setAnimationsEnabled:YES];
if(UIInterfaceOrientationLandscapeLeft)
[self performSegueWithIdentifier:#"landscapeView" sender:self];
}
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
Any suggestions? Thanks in advance.
You need to configure the view that is animating out at rotation time to not respond to changes in orientation/size. See, for example, your autolayout settings in interface builder, or possibly the struts/springs settings, if you're not using autolayout (which a newer technology).
I've got a view controller listening for both UIKeyboardWillShowNotification and UIKeyboardWillHideNotification. The handlers for these notifications adjust various parts of the view, which is standard procedure.
The following code is used to convert the keyboard rect from screen coordinates:
CGRect keyboardBounds = [self.view convertRect:[keyboardBoundsValue CGRectValue] fromView:nil];
Again, standard procedure. Unfortunately, there is a critical situation where this conversion fails. Look at what happens when an iPhone is rotated from portrait to landscape while the keyboard is deployed:
1) iOS automatically fires UIKeyboardWillHideNotification; self.interfaceOrientation is reported as portrait; keyboardBounds.height is 216.0. This makes sense. Why? Because the notification handler is given the chance to "clean up" before the view switches to landscape mode.
2) iOS automatically fires UIKeyboardWillShowNotification; self.interfaceOrientation is reported as portrait; keyboardBounds.height is 480.0. This does NOT make sense. Why not? Because the notification handler is going to do its work thinking that the height of the keyboard is 480.0!
Did Apple drop the ball on this one, or am I doing something wrong?
Please note that listening instead for UIKeyboardDidShowNotification is not a valid solution, because it significantly degrades the user experience. Why? Because animating my changes to the view after the keyboard deployment animation occurs is... well, pretty terrible-looking.
Has anyone managed to get autorotation working perfectly while the keyboard is deployed? It seems like an explosion of chaos that Apple has completely overlooked. >:|
Maybe a bit late, but I've just run into the same issue and have a nice solution for it that avoids any kind of work arounds (unless of course apple change things)
Basically, when the notification center calls your method for UIKeyboardWillShowNotification (or any of the other notifications), the frame that it gives you for UIKeyboardFrameBeginUserInfoKey is in context of the window, NOT your view. The problem with this, is that the windows coordinate system is always in portrait, regardless of the devices orientation, hence you're finding the width and height the wrong way round.
If you want to avoid your work around, simply convert the rectangle into the coordinate system of your view (which does change according to the orientation). To do this, do something like the following :
- (void) keyboardWillShow:(NSNotification *)aNotification
{
CGRect keyboardFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect convertedFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
......
/* Do whatever you want now with the new frame.
* The width and height will actually be correct now
*/
......
}
Hopefully this should be what you're after :)
Recently I've wrote a blog post about this exact problem you've described and how to solve it with a short and elegant way. Here is the link to the post: Synchronizing rotation animation between the keyboard and the attached view
If you don't want to dive into the long explanation described in the blog post here is a short description with a code example:
The basic principle is to use the same method that everyone uses - observing keyboard notifications to animate the attached view up and down. But in addition to that, you have to cancel these animations when the keyboard notifications are fired as a consequence of interface orientation change.
Rotation example without animation cancellation custom on interface orientation change:
Rotation example with animation cancellation on interface orientation change:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(adjustViewForKeyboardNotification:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(adjustViewForKeyboardNotification:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
self.animatingRotation = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
self.animatingRotation = NO;
}
- (void)adjustViewForKeyboardNotification:(NSNotification *)notification {
NSDictionary *notificationInfo = [notification userInfo];
// Get the end frame of the keyboard in screen coordinates.
CGRect finalKeyboardFrame = [[notificationInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// Convert the finalKeyboardFrame to view coordinates to take into account any rotation
// factors applied to the window’s contents as a result of interface orientation changes.
finalKeyboardFrame = [self.view convertRect:finalKeyboardFrame fromView:self.view.window];
// Calculate new position of the commentBar
CGRect commentBarFrame = self.commentBar.frame;
commentBarFrame.origin.y = finalKeyboardFrame.origin.y - commentBarFrame.size.height;
// Update tableView height.
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.size.height = commentBarFrame.origin.y;
if (!self.animatingRotation) {
// Get the animation curve and duration
UIViewAnimationCurve animationCurve = (UIViewAnimationCurve) [[notificationInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
NSTimeInterval animationDuration = [[notificationInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Animate view size synchronously with the appearance of the keyboard.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
[UIView setAnimationBeginsFromCurrentState:YES];
self.commentBar.frame = commentBarFrame;
self.tableView.frame = tableViewFrame;
[UIView commitAnimations];
} else {
self.commentBar.frame = commentBarFrame;
self.tableView.frame = tableViewFrame;
}
}
This answer was also posted in similar question: UIView atop the Keyboard similar to iMessage App
I met the same problem. iOS gaves me incorrect width/height of the keyboard. I used the following snipped in a keyboardDidShow handler:
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGSize keyboardSize2 = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
LogDbg(#"keyboard size: frameBegin=%#; frameEnd=%#", NSStringFromCGSize(keyboardSize), NSStringFromCGSize(keyboardSize2));
and for portrait and landscape modes of iPad I got respectively:
2012-06-14 04:09:49.734 -[LoginViewController keyboardDidShow:] 132 [DBG]:keyboard size: frameBegin={768, 264}; frameEnd={768, 264}
2012-06-14 04:10:07.971 -[LoginViewController keyboardDidShow:] 132 [DBG]:keyboard size: frameBegin={352, 1024}; frameEnd={352, 1024}
Guessing that the width of the keyboard should be greater then the height (yep, i'm so naive) I made a workaround like following:
if (keyboardSize.width < keyboardSize.height)
{
// NOTE: fixing iOS bug: http://stackoverflow.com/questions/9746417/keyboard-willshow-and-willhide-vs-rotation
CGFloat height = keyboardSize.height;
keyboardSize.height = keyboardSize.width;
keyboardSize.width = height;
}
Well, try looking at keyboard width. If it is the value that you are expecting, then I assume that the values are simply switched ;). 480 makes sense as a keyboard width for going into landscape, which is what gives me this hunch.
If that fails, just store the portrait and landscape rectangles separately. They are well documented ;)
I know this a very very late reply. Now only I came on this situation and find the unanswered question. So I thought I'll share my solution. There will be some other better way, but the following way also we can solve this.
The KBKeyboardHandler that I used is from: UITextField: move view when keyboard appears
I just changed my delegate as following:
- (void)keyboardSizeChanged:(CGSize)delta
{
CGRect frame = self.view.frame;
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (interfaceOrientation) {
case UIInterfaceOrientationPortrait:
frame.origin.y-=delta.height;
break;
case UIInterfaceOrientationPortraitUpsideDown:
frame.origin.y+=delta.height;
break;
case UIInterfaceOrientationLandscapeLeft:
frame.origin.x-=delta.height;
break;
case UIInterfaceOrientationLandscapeRight:
frame.origin.x+=delta.height;
break;
default:
break;
}
self.view.frame = frame;
}
And it was working fine.
Here is my workaround:
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float keyboardHeight = self.interfaceOrientation == UIInterfaceOrientationPortrait ? keyboardSize.height : keyboardSize.width;
Hope this helps :)
I use the following code to get the size of the keyboard which works fine for all rotations
NSDictionary *info = [aNotification userInfo];
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
kbHeight = [[NSNumber numberWithFloat:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.width] floatValue];
else
kbHeight = [[NSNumber numberWithFloat:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height] floatValue];
NSLog(#"keyboard height = %F",kbHeight);
I then test for the orientation using the status bar orientation (which works in the first launch case for the iPad) and shift the view in the relative direction needed to make space for the keyboard. This works perfectly, if the keyboard is visible then it relocates to the correct position on rotations.
UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (orientation == UIDeviceOrientationPortrait)
{
NSLog(#"Orientation: portrait");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x, self.originalCenter.y-kbHeight);
}
if (orientation == UIDeviceOrientationPortraitUpsideDown)
{
NSLog(#"Orientation: portrait upside down");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x, self.originalCenter.y+kbHeight);
}
if (orientation == UIDeviceOrientationLandscapeLeft)
{
NSLog(#"Orientation: landscape left");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x+kbHeight,self.originalCenter.y);
}
if (orientation == UIDeviceOrientationLandscapeRight)
{
NSLog(#"Orientation: landscape right");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x-kbHeight,self.originalCenter.y);
}
You can return the view to its original position when the keyboard disappears or via a textFileDidEndEditing function.
I've created a custom UIAlertView (by subclassing it and messing around with its show function) that has some custom subviews and is of non-standard size.
It works ok when I create and display it, however, when the device is rotated, the alert rotates and then returns to its default size.
Any ideas what functions to override - or should I tweak the UIViewController?
thanks,
Peter
Not sure if force rotating the UIAlertView fits the Apple GUI guidelines, but you can rotate it by defining the status bar (status bar and UIAlertView sticks together)
application.statusBarOrientation = UIInterfaceOrientationLandscapeRight;
application.statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
But UIAlertView is a UIView just like many others, so try this :
- (void)didPresentAlertView:(UIAlertView *)alertView
{
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationDuration:0.1];
alertView.transform = CGAffineTransformRotate(alertView.transform, degreesToRadian(90));
[UIView commitAnimations];
}
I have an app where I would like to support device rotation in certain views but other don't particularly make sense in Landscape mode, so as I swapping the views out I would like to force the rotation to be set to portrait.
There is an undocumented property setter on UIDevice that does the trick but obviously generates a compiler warning and could disappear with a future revision of the SDK.
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
Are there any documented ways to force the orientation?
Update: I thought I would provide an example as I am not looking for shouldAutorotateToInterfaceOrientation as I have already implemented that.
I want my app to support landscape and portrait in View 1 but only portrait in View 2. I have already implemented shouldAutorotateToInterfaceOrientation for all views but if the user is in landscape mode in View 1 and then switches to View 2, I want to force the phone to rotate back to Portrait.
This is long after the fact, but just in case anybody comes along who isn't using a navigation controller and/or doesn't wish to use undocumented methods:
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];
It is sufficient to present and dismiss a vanilla view controller.
Obviously you'll still need to confirm or deny the orientation in your override of shouldAutorotateToInterfaceOrientation. But this will cause shouldAutorotate... to be called again by the system.
If you want to force it to rotate from portrait to landscape here is the code. Just note that you need adjust the center of your view. I noticed that mine didn't place the view in the right place. Otherwise, it worked perfectly. Thanks for the tip.
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
self.view.center = CGPointMake(160.0f, 240.0f);
[UIView commitAnimations];
}
From what I can tell, the setOrientation: method doesn't work (or perhaps works no longer). Here's what I'm doing to do this:
first, put this define at the top of your file, right under your #imports:
#define degreesToRadian(x) (M_PI * (x) / 180.0)
then, in the viewWillAppear: method
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
if you want that to be animated, then you can wrap the whole thing in an animation block, like so:
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
[UIView commitAnimations];
Then, in your portrait mode controller, you can do the reverse - check to see if its currently in landscape, and if so, rotate it back to Portrait.
I was having an issue where I had a UIViewController on the screen, in a UINavigationController, in landscape orientation. When the next view controller is pushed in the flow, however, I needed the device to return to portrait orientation.
What I noticed, was that the shouldAutorotateToInterfaceOrientation: method isn't called when a new view controller is pushed onto the stack, but it is called when a view controller is popped from the stack.
Taking advantage of this, I am using this snippet of code in one of my apps:
- (void)selectHostingAtIndex:(int)hostingIndex {
self.transitioning = YES;
UIViewController *garbageController = [[[UIViewController alloc] init] autorelease];
[self.navigationController pushViewController:garbageController animated:NO];
[self.navigationController popViewControllerAnimated:NO];
BBHostingController *hostingController = [[BBHostingController alloc] init];
hostingController.hosting = [self.hostings objectAtIndex:hostingIndex];
[self.navigationController pushViewController:hostingController animated:YES];
[hostingController release];
self.transitioning = NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
if (self.transitioning)
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
else
return YES;
}
Basically, by creating an empty view controller, pushing it onto the stack, and immediately popping it off, it's possible to get the interface to revert to the portrait position. Once the controller has been popped, I just push on the controller that I intended to push in the first place. Visually, it looks great - the empty, arbitrary view controller is never seen by the user.
There is a simple way to programmatically force iPhone to the necessary orientation - using two of already provided answers by kdbdallas, Josh :
//will rotate status bar
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
//will re-rotate view according to statusbar
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];
works like a charm :)
EDIT:
for iOS 6 I need to add this function:
(works on modal viewcontroller)
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight);
}
I've been digging and digging looking for a good solution to this. Found this blog post that does the trick: remove your outermost view from the key UIWindow and add it again, the system will then re-query the shouldAutorotateToInterfaceOrientation: methods from your viewcontrollers, enforcing the correct orientation to be applied.
See it : iphone forcing uiview to reorientate
This is no longer an issue on the later iPhone 3.1.2 SDK. It now appears to honor the requested orientation of the view being pushed back onto the stack. That likely means that you would need to detect older iPhone OS versions and only apply the setOrientation when it is prior to the latest release.
It is not clear if Apple's static analysis will understand that you are working around the older SDK limitations. I personally have been told by Apple to remove the method call on my next update so I am not yet sure if having a hack for older devices will get through the approval process.
Josh's answer works fine for me.
However, I prefer posting an "orientation did change, please update UI" notification. When this notification is received by a view controller, it calls shouldAutorotateToInterfaceOrientation:, allowing you to set any orientation by returning YES for the orientation you want.
[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:nil];
The only problem is that this forces a re-orientation without an animation. You would need to wrap this line between beginAnimations: and commitAnimations to achieve a smooth transition.
Hope that helps.
FWIW, here's my implementation of manually setting orientation (to go in your app's root view controller, natch):
-(void)rotateInterfaceToOrientation:(UIDeviceOrientation)orientation{
CGRect bounds = [[ UIScreen mainScreen ] bounds ];
CGAffineTransform t;
CGFloat r = 0;
switch ( orientation ) {
case UIDeviceOrientationLandscapeRight:
r = -(M_PI / 2);
break;
case UIDeviceOrientationLandscapeLeft:
r = M_PI / 2;
break;
}
if( r != 0 ){
CGSize sz = bounds.size;
bounds.size.width = sz.height;
bounds.size.height = sz.width;
}
t = CGAffineTransformMakeRotation( r );
UIApplication *application = [ UIApplication sharedApplication ];
[ UIView beginAnimations:#"InterfaceOrientation" context: nil ];
[ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
self.view.transform = t;
self.view.bounds = bounds;
[ UIView commitAnimations ];
[ application setStatusBarOrientation: orientation animated: YES ];
}
coupled with the following UINavigationControllerDelegate method (assuming you're using a UINavigationController):
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
// rotate interface, if we need to
UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
BOOL bViewControllerDoesSupportCurrentOrientation = [ viewController shouldAutorotateToInterfaceOrientation: orientation ];
if( !bViewControllerDoesSupportCurrentOrientation ){
[ self rotateInterfaceToOrientation: UIDeviceOrientationPortrait ];
}
}
That takes care of rotating the root view according to whether an incoming UIViewController supports the current device orientation. Finally, you'll want to hook up rotateInterfaceToOrientation to actual device orientation changes in order to mimic standard iOS functionality. Add this event handler to the same root view controller:
-(void)onUIDeviceOrientationDidChangeNotification:(NSNotification*)notification{
UIViewController *tvc = self.rootNavigationController.topViewController;
UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
// only switch if we need to (seem to get multiple notifications on device)
if( orientation != [[ UIApplication sharedApplication ] statusBarOrientation ] ){
if( [ tvc shouldAutorotateToInterfaceOrientation: orientation ] ){
[ self rotateInterfaceToOrientation: orientation ];
}
}
}
Finally, register for UIDeviceOrientationDidChangeNotification notifications in init or loadview like so:
[[ NSNotificationCenter defaultCenter ] addObserver: self
selector: #selector(onUIDeviceOrientationDidChangeNotification:)
name: UIDeviceOrientationDidChangeNotification
object: nil ];
[[ UIDevice currentDevice ] beginGeneratingDeviceOrientationNotifications ];
This works for me (thank you Henry Cooke):
The aim for me was to deal with landscape orientations changes only.
init method:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
- (void)orientationChanged:(NSNotification *)notification {
//[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
CGRect bounds = [[ UIScreen mainScreen ] bounds ];
CGAffineTransform t;
CGFloat r = 0;
switch ( orientation ) {
case UIDeviceOrientationLandscapeRight:
r = 0;
NSLog(#"Right");
break;
case UIDeviceOrientationLandscapeLeft:
r = M_PI;
NSLog(#"Left");
break;
default:return;
}
t = CGAffineTransformMakeRotation( r );
UIApplication *application = [ UIApplication sharedApplication ];
[ UIView beginAnimations:#"InterfaceOrientation" context: nil ];
[ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
self.view.transform = t;
self.view.bounds = bounds;
[ UIView commitAnimations ];
[ application setStatusBarOrientation: orientation animated: YES ];
}
I have an app where I would like to support device rotation in certain views but other don't particularly make sense in Landscape mode, so as I swapping the views out I would like to force the rotation to be set to portrait.
I realise that the above original post in this thread is very old now, but I had a similar problem to it - ie. all of the screens in my App are portrait only, with the exception of one screen, which can be rotated between landscape and portrait by the user.
This was straightforward enough, but like other posts, I wanted the App to automatically return to portrait regardless of the current device orientation, when returning to the previous screen.
The solution I implemented was to hide the Navigation Bar while in landscape mode, meaning that the user can only return to previous screens whilst in portrait. Therefore, all other screens can only be in portrait.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)pInterfaceOrientation {
BOOL lHideNavBar = self.interfaceOrientation == UIInterfaceOrientationPortrait ? NO : YES;
[self.navigationController setNavigationBarHidden:lHideNavBar animated:YES];
}
This also has the added benefit for my App in that there is more screen space available in landscape mode. This is useful because the screen in question is used to display PDF files.
Hope this helps.
I solved this quite easily in the end. I tried every suggestion above and still came up short, so this was my solution:
In the ViewController that needs to remain Landscape (Left or Right), I listen for orientation changes:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification object:nil];
Then in didRotate:
- (void) didRotate:(NSNotification *)notification
{ if (orientationa == UIDeviceOrientationPortrait)
{
if (hasRotated == NO)
{
NSLog(#"Rotating to portait");
hasRotated = YES;
[UIView beginAnimations: #"" context:nil];
[UIView setAnimationDuration: 0];
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-90));
self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
self.view.frame = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
[UIView commitAnimations];
}
}
else if (UIDeviceOrientationIsLandscape( orientationa))
{
if (hasRotated)
{
NSLog(#"Rotating to lands");
hasRotated = NO;
[UIView beginAnimations: #"" context:nil];
[UIView setAnimationDuration: 0];
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(0));
self.view.bounds = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
self.view.frame = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
[UIView commitAnimations];
}
}
Keep in mind any Super Views/Subviews that use autoresizing, as the view.bounds/frame are being reset explicitly...
The only caveat to this method for keeping the view Landscape, is the inherent animation switching between orientations that has to occur, when it would be better to have it appear to have no change.
iOS 6 solution:
[[[self window] rootViewController] presentViewController:[[UIViewController alloc] init] animated:NO completion:^{
[[[self window] rootViewController] dismissViewControllerAnimated:NO completion:nil];
}];
The exact code depends per app and also where you place it (I used it in my AppDelegate). Replace [[self window] rootViewController] with what you use. I was using a UITabBarController.
I found a solution and wrote something in french (but code are in english). here
The way is to add the controller to the window view (the controller must possess a good implementation of the shouldRotate.... function).
If you are using UIViewControllers, there is this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
Return NO for the view controllers containing the views you don't want to rotate.
More info here
I don't think this is possible to do at run-time, though you could of course just apply a 90 degree transform to your UI.
This is what I use. (You get some compile warnings but it works in both the Simulator and the iPhone)
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];