Manually rotate viewcontroller - iphone

My app must NOT auto-rotate at all. But it includes a screen which tells the user to rotate his phone (and not the opposite!).
To do that, the ViewController must make an animated rotation (without any rotation event) when the screen is displaying.
So I used
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:animated];
[super viewWillDisappear:animated];
}
And
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
to make my screen rotate, as every website and documentation recommend.
But only the StatusBar rotates: my NavigationBar remains stuck at the top.

I would use a CGAffineTransform perhaps on the navigationcontroller view? Simply rotate it using an animation block 90 degrees?

this code is helpful for you to resize the navigation bar automatically you can use it in where you create the navigationController & navigation bar
self.navigationController.navigationBar.autoresizesSubviews = YES;
self.navigationController.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
above code will work automatically if it is not then you try this will work in all delegates methods of your view controller where you need the change
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:animated];
[self.navigationController shouldAutorotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
CGRect frame = self.navViewController.navigationBar.frame;
frame.size = self.view.frame.size;
if (toInterfaceOrientation == UIInterfaceOrientationPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
frame.size.height = 44;
} else {
frame.size.height = 32;
}
self.navViewController.navigationBar.frame = frame;
if navigation controller is rootview controller then check it enables the all orientation supports
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
[super shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
[self.navigationController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
return YES;
}
you can use this code in viewcontroller delegates listed below according to your requirment
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillAppear:(BOOL)animated
– willRotateToInterfaceOrientation:duration:
– willAnimateRotationToInterfaceOrientation:duration:
– didRotateFromInterfaceOrientation:

Related

UIImagePickerControllerCameraDeviceFront works every other time

This question is very similar to an existing question asked here UIImagePickerControllerCameraDeviceFront only works every other time I tried the solution presented but it didn't work for me
I have a simplest of a project with two view controllers. In the blue one I am displaying a small UIView with a UIImagePickerController in it. NOTE: I am displaying front facing camera when app is launched.
I hit the next button and go to orange view controller and when I hit the back button and come back to blue view controller the UIImagePickerController flips from Front to rear. I guess the reason is that it thinks its busy and moves to the rear cam. If I keep moving back and forth between the view controllers the camera keeps flipping front, back, front, back, front, back...
Here is my code and screenshots, what am I doing wrong?
In my *.h
#import <UIKit/UIKit.h>
#interface v1ViewController : UIViewController <UIImagePickerControllerDelegate>
{
UIImagePickerController *picpicker;
UIView *controllerView;
}
#property (nonatomic, retain) UIImagePickerController *picpicker;
#property (nonatomic, retain) UIView *controllerView;
#end
In my *.m file (This code is only used when blue colored view controller is displayed)
#import "v1ViewController.h"
#import <MobileCoreServices/UTCoreTypes.h>
#implementation v1ViewController
#synthesize picpicker;
#synthesize controllerView;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
controllerView = picpicker.view;
[controllerView setFrame:CGRectMake(35, 31, 250, 250)];
controllerView.alpha = 0.0;
controllerView.transform = CGAffineTransformMakeScale(1.0, 1.0);
[self.view addSubview:controllerView];
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
controllerView.alpha = 1.0;
}
completion:nil
];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
You are dismissing the controller in both the viewDidDisappear and viewWillDisappear methods.
That could be the cause of your problem.
Although I do not have a device with a camera available right now to verify this, it seems that you're not dismissing the pickerview controller correctly. The documentation states that you should call dismissModalViewControllerAnimated: on the parent controller in order to dismiss the picker (though, calls to presented controllers will propagate to presenters - so this is not the problem), but in your case you're not displaying the controller modally in the first place so it will not work.
What I would try in this case is to release the picker instead (if not under ARC) and set it to nil (instead of calling [picpicker dismissModalViewControllerAnimated:YES];).
PS. In fact, it seems that there is a bigger problem with your design. Since each button is set to present the other party modally you are not dismissing any of the controllers ever. The controllers just keep stacking on each other. You should either consider to embed them in a navigation controller and have it handle the hierarchy or just set dismissModalViewControllerAnimated: (dismissViewControllerAnimated:completion: on iOS5+) as the action of the second controller's button instead of a modal segue.
This is a very simple issue. I don't know why this happens exactly, but it seems that UIImagePickerController was designed to recreated each time it's needed instead of keeping any reference to it, which seems logical if you think about it. Basically, you need to recreate and reconfigure your picker each time. Below I've pasted some code to give an image of what I mean.
Simple solution:
- (UIImagePickerController *)loadImagePicker {
UIImagePickerController *picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
return picpicker;
}
and in:
-(void)viewWillAppear:(BOOL)animated{
if(!self.picpicker){
self.picpicker = [self loadImagePicker];
[self.view addSubview: self.picpicker];
}
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.picpicker removeFromSuperview];
self.picpicker = nil;
}

setStatusBarOrientation issue

I have a navigation controller app. And first I push FirstViewController (support Portrait orientation) and then SecondViewController (supports all orientations). When I'm in landscape mode of SecondViewController and press back button, FirstViewController appears in landscape mode. That's why I manually rotate the navigation view, but when I want to set setStatusBarOrientation to Portrait (First view controller should appears only in portrait mode), the orientation of view controller is still landscape, and even if rotate the device to portrait mode, the orientation stay landscape
.Here is my code of FirstViewController:
- (void)viewWillAppear:(BOOL)animated
{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
{
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
prevInterfaceOrientation = UIInterfaceOrientationLandscapeLeft;
self.navigationController.view.transform = CGAffineTransformIdentity;
self.navigationController.view.transform =
CGAffineTransformMakeRotation(degreesToRadians(90));
}
else if (self.interfaceOrientation ==
UIInterfaceOrientationLandscapeRight) {
prevInterfaceOrientation = UIInterfaceOrientationLandscapeRight;
self.navigationController.view.transform = CGAffineTransformIdentity;
self.navigationController.view.transform =
CGAffineTransformMakeRotation(degreesToRadians(-90));
}
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];
[self.tableViewDetail reloadData];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (toInterfaceOrientation == UIInterfaceOrientationPortrait)
{
if (prevInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
self.navigationController.view.transform = CGAffineTransformIdentity;
}
else if (prevInterfaceOrientation ==
UIInterfaceOrientationLandscapeRight) {
self.navigationController.view.transform = CGAffineTransformIdentity;
}
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
[self.tableViewDetail reloadData];
}
}
I even tried to use:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (UIInterfaceOrientationIsLandscape(prevInterfaceOrientation))
{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
}
}
but self.interfaceOrientation still stays landscape, when I rotate to portrait.
But I really need to rotate the view to portrait mode manually to allow users to see,that FirstViewController suppors only portrait orientation.
I have the option to put the SecondViewController's view on MainWindow (like modal window), but I don't like this idea, because if apple has setStatusBarOrientation method, it seems to me, that it has to be right solve of this issue.
I would get rid of the transformations, and use
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:animated];
This in combination with the forced redrawing of the view stack will get it done. This can be done by adding the following to viewDidAppear to the first controller (it doesn't work in viewWillApear).
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
if ([window.subviews count] > 0) {
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window insertSubview:view atIndex:0];
}
else {
DLog(#"NO view to force rotate?");
}
Unfortunately, the transition animation is not very clean when you do this, so I would recommend taking a snapshot of the portrait screen, overlay this over your view, and then fade it out with a separate animation.
Steven Veltema's answer didn't work for me.
I had one view controller where all orientations where allowed, and the rest only supported portrait. When i had the first view controller in landscape and navigated to another view controller, the orientation didn't refresh as you are experiencing.
I found another trick to reload the views i correct orientation. Just add a modal view controller you don't even see it. Add this in all other views:
- (void)viewDidAppear:(BOOL)animated
{
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
UIViewController * viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[viewController dismissModalViewControllerAnimated:NO];
}
[super viewDidAppear:animated];
....
}
Another quick solution is
Click on your project in the left side bar.
In General settings, choose "Hide during application launch" option.

Displaying Game Center modal view in landscape

I am using cocos2d engine in landscape-only orientation with no autorotation.
I want to display standart GC achievements modal view.
It's showing up normally, from bottom of screen (while holding device in landscape), but it is dismissing to the right side of the screen (like portrait modal views). It's seems to be changing orientation for dismiss animation, but view is not rotating before this. It's just sliding to the right.
Also I get a warning in console:
Unbalanced calls to begin/end appearance transitions for <UIViewController: 0x41b9a0>.
That's how I showing this controller:
-(void)showAchievements:(id) sender {
utilityController = [[UIViewController alloc] initWithNibName:nil bundle:nil];
[[[CCDirector sharedDirector] openGLView] addSubview:utilityController.view];
GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
if (achievements != nil)
{
achievements.achievementDelegate = self;
[utilityController presentModalViewController: achievements animated: YES];
}
[achievements release];
}
- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
[utilityController dismissModalViewControllerAnimated:YES];
[utilityController release];
}
In gameConfig.h I have following configuration:
#define GAME_AUTOROTATION kGameAutorotationNone
Tried to change this to kGameAutorotationCCDirector - same thing. kGameAutorotationUIViewController - uiviews are jumping all over the screen.
Please do not suggest rotating UIView with CGAffineTransformMakeRotation - it's just a hack...
For Kobold2D I solved this for all orientations with a category for the GKMatchmakerViewController. You can make the same category for the other Game Center view controllers.
This will force the GC views to use the current device orientation:
#implementation GKMatchmakerViewController (OrientationFix)
-(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// [RootViewController option removed for clarity]
// all other autorotation types use the current device orientation
ccDeviceOrientation orientation = [CCDirector sharedDirector].deviceOrientation;
switch (interfaceOrientation)
{
case UIInterfaceOrientationPortrait:
return (orientation == kCCDeviceOrientationPortrait);
break;
case UIInterfaceOrientationPortraitUpsideDown:
return (orientation == kCCDeviceOrientationPortraitUpsideDown);
break;
case UIInterfaceOrientationLandscapeLeft:
return (orientation == kCCDeviceOrientationLandscapeRight);
break;
case UIInterfaceOrientationLandscapeRight:
return (orientation == kCCDeviceOrientationLandscapeLeft);
break;
default:
break;
}
return NO;
}
#end
This is my first ever answer on Stackoverflow, so please forgive me if any formatting may not work.
Anyway, on to the code. This solution works for me, for the Achievements-Viewcontroller. Any other GK-Viewcontroller may work accordingly...
#implementation GKAchievementViewController (OrientationFix)
-(BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
#end
#interface GKLeaderboardViewController (OrientationFix)
#end
-(void)showAchievements
{
GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
if (achievements != nil)
{
achievements.achievementDelegate = self;
[self presentModalViewController: achievements animated: YES];
}
[achievements release];
}
- (void)achievementViewControllerDidFinish: (GKAchievementViewController *)viewController
{
AppDelegate *delegate = [UIApplication sharedApplication].delegate;
[delegate.viewController dismissModalViewControllerAnimated:YES];
}
Try to call
self.modalPresentationStyle = UIModalPresentationCurrentContext;
Before presenting the ViewController

UINavigationController loading view incorrectly due to Orientation/Shake

Background: App has a shake to go home feature. Home view Only supports portrait.
If you shake a bit harder than usual, the view that you are on starts to rotate (which is fine) , but then it detects a shake and does a popViewControlller to the home view. When it does this it loads the navigation controller just fine, but the view under (the home content) gets loaded behind the bar and is stretched up (it's basically loading underneath the navigation bar, so it gets stretched up)
The back button handles this just fine from landscape to portrait (since its not mid transitions)
How should I go about handling this orientation change (from the shake) so I can pop back into the root view controller, without the view loading under the navigation bar?
Edit:What's happening is the content thinks that it has the entire view to load, so it stretches itself to take the entire screen, not realizing theres a navigationbar above it. I can tell since the images loading are stretched out
added a bounty of 50.
Edit Here's How I'm detecting Shakes and Popping
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
UINavigationController *navController = self.navigationController;
[[self retain] autorelease];
HomeViewController *home = [[HomeViewController alloc]init];
[navController popViewControllerAnimated:YES];
home.title =#"Home View Controller";
[home release];
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
Here's my App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
navController = [[UINavigationController alloc]init];
[self.window addSubview:navController.view];
HomeViewController *home = [[HomeViewController alloc]init];
[[self home] setFrame:[[UIScreen mainScreen] applicationFrame]];
I'll include a mockup here.
Normal View:
Stretched View After a Shake/Pop:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
I'm a bit puzzled by your code so I'd really suggest starting from the beginning. As Lukya mentioned, there's no reason to recreate the HomeViewController. I'm also baffled by the "[[self retain] autorelease];" bit. That shouldn't be necessary unless you're doing something incorrectly elsewhere.
So I would start with this... In application:didFinishLaunchingWithOptions: do something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
HomeViewController *home = [[[HomeViewController alloc] init] autorelease];
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:home] autorelease];
[self.window addSubview:navController.view];
}
The window will retain a your nav controller and the nav controller will retain your HomeViewController.
Then in motionEnded:withEvent: do something like:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
That should be it.
If that does not work then can you give any other info? For example, does HomeViewController implement (and return YES) in shouldAutorotateToInterfaceOrientation:? If so, can you return no so it doesn't rotate since your first line says "Home view Only supports portrait"?
Edit: An example of willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation: as well.
In the header for whatever controller you're detecting shakes in add a boolean:
BOOL isRotating;
In your implementation file add the two UIViewController methods we want to override -- something like:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
isRotating = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
isRotating = NO;
}
Now, do something like this for your event handler:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake && !isRotating)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
in your home view controller's xib, go to the inspector for the view and set top bar as navigation bar.. and in view did load set self.navigationBarHidden = NO;...
NOTE:
there are many thing wrong with the code you've posted.. but none of them causes the orientation problem... in fact this seems to be the only code you need in that method:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[navController popViewControllerAnimated:YES];
}
}
so you might want to change this code as well..
Have you tried calling [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:YES]; in your home view controller? You could also try to place this in where you detect a shake.
I have come across this issue with underlapping the navigation bar. I am not sure what causes it but you can work around it by calling,
[[self loadingView] setFrame:[[UIScreen mainScreen] applicationFrame]];
after the problem view is added to window in the application delegate.

Making one specific class of view controller auto rotate in a tab bar app, but forcing all other classes of view controller to stay portrait

I have a tab bar controller with this code
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
//NSLog(#"object type %#" ,nil);
if([[self navigationController ] isKindOfClass:[UINavigationController class]])
if([[[self navigationController] visibleViewController] isKindOfClass:[SLImageViewController class]])
return YES;
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
I need any instance of the SLImageViewController class to rotate, but none of the others. I have done everything i can think of like adding return YES to my SLImageViewController and other fixes.
Can anyone tell me what I'm doing wrong?
You could accomplish this by:
setting statusBar orientation to viewWillAppear and viewWillDisappear
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear: animated];
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight];
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear: animated];
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait];
}
and rotating a view manually: self.view.transform = CGAffineTransformMakeRotation(M_PI/2);
presenting that view modaly will trigger shouldAutorotateToInterfaceOrientation method