iOS 8 - Rotation makes statusBar disappear even in portrait mode after toggling controls - objective-c

I'm having a lot of troubles with the new auto hiding of the status bar in iOS 8.
In my app, I've got a view in which when the user taps once, the navigation bar and the status bar disappear.
When in landscape, the status bar hides by itself and it's fine to me. I only need it in portrait mode.
But the problem is that when the device is landscape and the bar are shown, when the user taps twice to toggle the bar (so showing), and turns the device in portrait mode, the status bar is still hidden.
Basically I need to be able to hide the statusBar without interfere with its natural behavior on iOS 8, so I recap the scenario:
User enters said view with tabBar and NavigationBar and statusBar;
Taps once in the view and the bars disappear
User rotates the device, statusBar not appearing - OK, I want this
User taps again to show the bars - StatusBar still hidden, OK.
User rotates from landscape to portrait and..
statusBar still hidden - NOT OK.
MRW >
(source: mshcdn.com)
I've tried to adjust the statusBar on willRotate, but I made a mess in which the statusBar would be visible when it wasn't supposed to. Code I'm using:
- (BOOL)prefersStatusBarHidden
{
return statusBarHidden;
}
-(void)toggleBars:(UITapGestureRecognizer *)gesture{
CATransition *animation = [CATransition animation];
animation.type = kCATransitionFromBottom;
animation.subtype = kCATransitionFromTop;
animation.duration = .2f;
[animation setTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]];
BOOL toggleNavigationBar = self.navigationController.navigationBarHidden;
[self.navigationController.navigationBar.layer addAnimation:animation forKey:nil];
[self.navigationController setNavigationBarHidden:!toggleNavigationBar animated:YES];
BOOL toggleTabHidden = self.tabBarController.tabBar.hidden;
if(![[[NSUserDefaults standardUserDefaults] objectForKey:#"showTabBar"]isKindOfClass:[NSNull class]]){
if([(NSNumber*)[[NSUserDefaults standardUserDefaults] objectForKey:#"showTabBar"]boolValue])
{
[self.tabBarController.tabBar.layer addAnimation:animation forKey:nil];
[self.tabBarController setHideTabBar:!toggleTabHidden animated:YES];
}
}
statusBarHidden = [UIApplication sharedApplication].statusBarHidden;
[[UIApplication sharedApplication] setStatusBarHidden:!statusBarHidden withAnimation:UIStatusBarAnimationSlide];
[self setNeedsStatusBarAppearanceUpdate];
if (IS_IOS8){
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){
if (statusBarHidden){
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
}
}
}
I was thinking about setting a flag in which when the statusBar is hidden when in landscape and all the controls are there, on rotation it would trigger the statusBar. With no success, apparently..
Any help highly appreciated.

Are you using UIViewController-based status bar appearance?
If you implement prefersStatusBarHidden I assume you are.
Now,
[[UIApplication sharedApplication] setStatusBarHidden:!statusBarHidden withAnimation: UIStatusBarAnimationSlide];
is not supposed to work with UIViewController-based status bar appearance.
You need just to return different value from prefersStatusBarHidden method and call setNeedsStatusBarAppearanceUpdate to notify the app that returning value has changed.
So to change statusbar visibility you should just do
#property (nonatomic, assign) BOOL hideStatusBar;
- (BOOL)prefersStatusBarHidden
{
return self.hideStatusBar;
}
- (void)toggleBars:(UITapGestureRecognizer *)gesture
{
... hide navbar and tabbar ...
self.hideStatusBar = ! self.hideStatusBar;
[self setNeedsStatusBarAppearanceUpdate];
}
And that's it!

This worked for me:
- (void)viewWillLayoutSubviews {
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
}

This is the swift (3.0) version of that prefersStatusBarHidden
override var prefersStatusBarHidden: Bool{
return false
}
You just need to add it to your ViewController

#pragma mark After and Before Oriantation Change Methods+Delegate
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
}
#pragma mark nav
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
This is Short and Easy method.

Related

UIImagePickerController breaks status bar appearance

In my .plist file, I have "View controller-based status bar appearance" set to NO. But after UIImagePickerController, my app behaves as if the option is set to YES.
In my app, I present a VC that presents a UIImagePickerController.
The problem happens like this:
After photo picker is presented, when a photo library is picked, the color of the status bar text changes.
Then once, UIImagePickerController is dismissed, status bar spacing
changes for the rest of my app and all the navigation bar for other controllers displays under the status bar.
Is there a way to solve this without managing status bar in my view controllers?
None of the solutions above worked for me, but by combining Rich86man's and iOS_DEV_09's answers I've got a consistently working solution:
UIImagePickerController* imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
and
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
Regarding this awesome solution. For 2014 / iOS8 I found in some cases you need to ALSO include prefersStatusBarHidden and, possibly, childViewControllerForStatusBarHidden So...
-(void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
-(BOOL)prefersStatusBarHidden // iOS8 definitely needs this one. checked.
{
return YES;
}
-(UIViewController *)childViewControllerForStatusBarHidden
{
return nil;
}
-(void)showCamera
{
self.cameraController = [[UIImagePickerController alloc] init];
self.cameraController.delegate = (id)self; // dpjanes solution!
etc...
I faced this same issue today. Here is my solution.
In the view controller who calls the image picker, set yourself as the delegate of the image Picker. (You're probably already doing this)
UIImagePickerController* imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
Since UIImagePickerController is a type of Navigation controller, you're also setting yourself as the UINavigationController delegate. Then :
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
Replace UIStatusBarStyleLightContent with whatever style you are looking for.
The accepted answer will work if you have the 'View controller-based status bar appearance' set to NO in your .plist file. If indeed you need to control the status bar in some other view controllers and have this option set to YES, the other way to make UIImagePickerController to behave correctly is by subclassing it
// .h
#interface MYImagePickerController : UIImagePickerController
#end
// .m
#implementation MYImagePickerController
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent; // change this to match your style
}
#end
i faced the same problem.
here is my solution.
put this in the viewWillAppear of the view controller from which you are opening the image pickerview
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
Can you try this. I think needsStatusBarApperanceUpdate will work.
1 -Set UIViewControllerBasedStatusBarAppearance to NO.
2- Call [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
3- [self setNeedsStatusBarAppearanceUpdate];
I found this to offer proper handling, there's two parts.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
...
the UIImagePickerController itself presents view controllers, so this delegate works for all presenters on the stack.
the viewWillAppear ensures this view controller itself is always reset whenever a presenting view controller dismisses above it.
I had the same problem.
Add in info plist: "View controller-based status bar appearance" with value "NO"
Example here https://stackoverflow.com/a/19211669
This solution works for me.
This is probably a bug. I solved the problem by setting "View controller-based status bar appearance" set to YES and in every view controller pasting in the following code:
- (BOOL)prefersStatusBarHidden
{
return YES;
}
Then my app behaves as expected.
For hiding the status bar in UIImagePicker :
-
(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
and when UIImagePicker is dismissed to hide the status bar in View controller use the following code :
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
try this ....
this will work in both cases i.e whether you use presentModalViewController and pushViewController
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
delegate methods
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[picker dismissViewControllerAnimated:YES completion:^{}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[picker dismissViewControllerAnimated:YES completion:nil];
}
All the above didn't work for me. I solved the issue by changing the presentation style to:
imagePickerController.modalPresentationStyle = UIModalPresentationFullScreen;
None of the above solutions worked for me.
I present UIImagePickerController as modal view controller. After dismissing UIImagePickerController the status bar state was:
[UIApplication sharedApplication].statusBarOrientation = 0 (UIDeviceOrientationUnknown)
[UIApplication sharedApplication].statusBarFrame = { 0, 0, 0, 0}
The solution that fixed the problem for me was restoring statusBarOrientation after dismissing UIImagePickerController:
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
[self.viewController presentViewController:cameraUI animated:true completion:^(void){ }];
...
[self.viewController dismissViewControllerAnimated:animated completion:^(void){
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
}];
This code helped me to customize status bar style.
EDIT: this solution works if "View controller-based status bar appearance" == YES
#implementation UIImagePickerController (IOS7_StatusBarStyle)
-(UIViewController*)childViewControllerForStatusBarStyle
{
return nil;
}
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
#end
All the answers above is ok and can help.
I had the same problem having to manage the application runned under different iOS versions.
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
if(IS_IOS8_AND_UP) {
imagePickerController.modalPresentationStyle = UIModalPresentationFullScreen;
} else {
imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
}
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:YES completion:nil];
Then, in delegate:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
/* Cancel button color */
_imagePicker.navigationBar.tintColor = <custom_color>
/* Status bar color */
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
}
Yet another solution which may work in some of the situations.
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .PhotoLibrary
imagePicker.navigationBar.barStyle = .Black
Have you tried calling [self setNeedsStatusBarAppearanceUpdate] when your presenting view controller reappears?
I try to hide the status bar in UIImagePickerController in iOS7, but I still don't know how to do this. I use
- (void)viewWillAppear:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:YES
withAnimation:UIStatusBarAnimationNone];
}
in the ViewController that call the UIImagePickerController, and set "View controller-based status bar appearance = NO" in the plist file. Hope this can help.
try this :
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
and in the protocol implement, use this:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
This solved it for me...:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[picker dismissViewControllerAnimated:YES completion:nil];
}
Nothing here specifically fixed the problem in that I was having (and perhaps that the OP was having too), so I thought I would share my answer. Instead of hiding the status bar which I think is a buggy solution (I noticed that it would sometimes leave my app in a state where the status bar was hidden when it shouldn't be). I instead opted to try and play nice with the UIStatusBarStyles.
When the UIImagePickerController has its view presented I set the status bar style to default since the default background color is a light grey.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}
Then, when the image picker is dismissed, I set it back to the UIStatusBarStyleLightContent.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//work
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
//work
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
[self dismissViewControllerAnimated:YES completion:NULL];
}
In this case,We are using 2 steps
In first step:
Add in info.plist: "View controller-based status bar appearance" with value "NO"
In Second step: Use/call this code with delegate of UIImagePickerController
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if([navigationController isKindOfClass:[UIImagePickerController class]])
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
In case of IOS-7 add One more Function
- (BOOL)prefersStatusBarHidden
{
return YES;
}
As of iOS 8.1, it seems like they've finally fixed this bug! I was able to remove all of the workarounds I employed from my code.
Using the default iOS 8 behaviour I was having problems with the status bar appearing when I wanted it hidden.
The solution I found was that, directly after calling presentPopover from my view controller I did:
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate) withObject:nil afterDelay:0.01];
I also had to add this to my main view controller:
- (UIViewController *)childViewControllerForStatusBarHidden
{
return nil;
}
So I had this problem and I was able to solve it by simply implementing a single delegate function. The background of my status bar is black, and so UIStatusBarStyle for my application is .LightContent. When I presented the UIImagePickerController to select a photo from the device storage, the status bar was fine. However, upon clicking into a directory such as "Camera Roll" or "Favorites," effectively pushing onto the navigation stack, the status bar disappeared. Upon selecting a photo, there was no status bar at all; upon dismissing another modal view controller, only the battery was present, indicating the rest of the status bar may be black as well.
I tried some of the other solutions such as extending UIImagePickerController, but in Swift, you cannot override using extensions. I then tried to subclass UIImagePickerController and tried to hide its status bar on viewWillAppear() and unhiding the status bar on viewWillDisappear. I was able to see the status bar hide with a .Slide animation, but since the status bar was invisible upon selecting a directory, I was not able to see the status bar unhide. Again, the green battery came back with the rest of the status bar invisible upon dismissing a modal view controller. I also tried overriding prefersStatusBarHidden(), but that function was never called, so I tried calling setNeedsStatusBarAppearanceUpdate() to ensure that prefersStatusBarHidden() is called by the system, but it still is not called. Also, there is the suggestion to set the status bar to be hidden on the delegate method navigationController willShowViewController. Once again, all this does is hide the status bar, which does not solve the problem. As it turns out, it seems that the status bar style is changed upon pushing onto the navigation stack of the UIImagePickerController. To solve the problem entirely, I did not have to write extensions or subclass UIImagePickerController. All you need to do is set the delegate and set the status bar style to remain the same. This addition made it as if the problem never existed.
let pickerController = UIImagePickerController()
pickerController.delegate = self
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false)
}
I actually found a better way to set the status bar background color in Image Picker. Basically you need to set the backgroundImage from the navigationBar to nil, because is default in Image Picker has a backgroundImage as a white Image.

iOS6 landscape app with only one controller multioriented popViewController issue

I have this setup to support only landscape orientation in most of my viewcontrollers
My app delegate has this piece of code:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSUInteger orientations = UIInterfaceOrientationMaskLandscape;
if (self.window.rootViewController) {
UIViewController * pressented = [[((UINavigationController *)self.window.rootViewController) viewControllers] lastObject];
orientations =[pressented supportedInterfaceOrientations];
}
return orientations;
}
And in most viewcontrollers this:
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
My problem comes when I push THIS controller (the one I would like to rotate):
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskALL;
}
It rotates perfectly BUT when I pop the viewcontroller (tap the back button of the navigation bar) with the orientation in portrait, the presenting viewcontroller also sets it's orientation to Portrait.
How can I make the presenting viewcontroller stays locked on landscape, or force the problematic controller to rotate back to landscape before popping.
Add this to your portrait view controller:
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
UIViewController* dummyController = [[UIViewController alloc] init];
[self presentViewController:dummyController animated:NO completion:^{
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
I know it's a hack, but it works. Anyone knows a better solution?

iOS 6 Orientation rotate automatically back to portrait on pressing done on MoviePlayer

I am using MoviePlayer controller to play a video in my iOS app. I am using orientation notification like this
if(deviceOrientation ==UIDeviceOrientationLandscapeLeft)
{
NSLog(#"Replay is in Landscape");
self.fullScreenFlag = YES;
[self.moviePlayer setFullscreen:YES animated:NO];
}
This makes my video screen to play in full screen when user turns the phone to landscape orientation. But when I press done button on moviePlayer control I go into following method
- (void)movieWillExitFullscreen:(NSNotification*)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if(deviceOrientation ==UIDeviceOrientationLandscapeLeft) {
NSLog(#"Pressed Done in Landscape");
//Problem: Here I want to force my VideoViewController to rotate back to portrait Mode
}
}
Not sure how can I make the VC to go back to portrait as soon as user pressed done button or video stops playing. I am aware to the moviePlayerNotificationMethods but what should I call in those method for orientation is not clear.
I solved this issue by having a separate view controller for the video playback.
So, you would have two view controllers
SomeViewController
MoviePlayerViewController
In your SomeViewController, when you want to play the movie:
MoviePlayerViewController *vc = [[MoviePlayerViewController alloc] initWithNibName:#"MoviePlayerViewController" bundle:nil];
[vc setPathToMovie:path];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
And then in your MoviePlayerViewController
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[[self navigationController] popViewControllerAnimated:YES];
}
You can then lock down your SomeViewController to portrait, and if the user is in landscape when watching the video, they will return to portrait when popping back to SomeViewController.
I never found a solution using the deviceOrientation method and a modal MPMoviePlayerController. There may be one though!
I solved this by doing this in "moviePlayBackDidFinish"
UIViewController* forcePortrait = [[UIViewController alloc] init];
[self presentViewController:forcePortrait animated:NO completion:^{
[forcePortrait dismissViewControllerAnimated:NO completion:nil];
}];
It's not beautiful but it works like a charm :-)
Depending upon whether you using MPMoviePlayerController within a ViewController or as a separate ViewController the answer is as follows :-
Firstly :- This link will explain you how to restrict some views to portrait and allow others to rotate?
In that link you will see that, in the NavigationViewController you have made, following changes:-
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
What it does is, it give the child to make their own decision if they want to auto-rotate or not.
Next the ViewController containing your MoviePlayer should do this :-
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Once you have done this, it gives the power of AutoRotation to your ViewController.
Now here's the tricky part, see I assume that you might have restricted your ViewController to Portrait, and since movie player allows you go fullscreen and in fullscreen when you rotate your screen it will turn to landscape, and now if you press done button it won't turn to portrait rather it will exit the fullscreen in landscape itself. In this case what you should do is, in your:-
- (BOOL)shouldAutorotate
{
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft) {
if ([[[self.navigationViewController.viewControllers] lastObject] class] == [MoviePlayerViewController class] ) {
return YES;
}
return NO;
}
return NO;
}
So, what it does is, you should auto-rotate only when the orientation is landscape and not when its portrait.
So far so good, next comes the MoviePlayer, considering that you have already played the Video and your only interest is when we click "Done" button it should auto-rotate to portrait.
Register for a notification to your MoviePlayer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerWillExitFullScreen:) name:MPMoviePlayerWillExitFullscreenNotification object:_moviePlayer];
Then in the selector:
- (void) moviePlayerWillExitFullScreen:(NSNotification*)notification{
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerWillExitFullscreenNotification object:_moviePlayer];
}
Tada! the magic is done! try out let me know ;-)

iOS: Change interface orientation when pop view controller

All view controllers in my app are working only in portrait orientation except one which can be portrait or landscape oriented.
I have some usage scenario like following:
I push controller which works in both orientations to UITabBarController
User change orientation from portait to landscape
User press "back button"
After these actions application remains in landscape orientation and does not change it automatically to portrait.
I control view controller orientation using supportedInterfaceOrientations (I use iOS 6.0). What I do wrong? How can I get correct behaviour when application automatically change orientation to allowed when user press back button? Thank you for answer!
In iOS 6 (and possibly earlier), if a view controller is offscreen when the device rotates, it does not get any notification. Nor does it get sent willAnimateRotationToInterfaceOrientation:duration: when it becomes the top view controller.
You need to keep track of the current orientation of the view controller and check the device orientation in viewWillAppear:. If they are different, you can use willAnimateRotationToInterfaceOrientation:duration: to set it correctly.
Since this is something you are likely to do a lot, you may want to create a generic superclass that your view controllers inherit from.
A typical solutions is:
#implementation MyHandlesOffscreenRotationController
{
BOOL isShowingPortrait;
}
- (void) viewDidLoad
{
[super viewDidLoad];
isShowingPortrait = UIInterfaceOrientationIsPortrait(
[[UIApplication sharedApplication] statusBarOrientation]);
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
BOOL currIsPortrait = UIInterfaceOrientationIsPortrait(
[[UIApplication sharedApplication] statusBarOrientation]);
if ((isShowingPortrait && !currIsPortrait) ||
(!isShowingPortrait && currIsPortrait)) {
[self willAnimateRotationToInterfaceOrientation:
[[UIApplication sharedApplication] statusBarOrientation]
duration:0.0f];
}
}
#end
Just override -(BOOL)shouldAutoRotate and - (NSUInteger)supportedInterfaceOrientations inside a UINavigationController category, then ViewController will force rotate to its supported orientation after pop from other ViewController.
#implementation UINavigationController (Rotate)
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
#end
iOS 9 and above
At the time of pop just write the below-mentioned code in your viewWillAppear method.
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationPortrait]forKey:#"orientation"];
With this, your view will appear in portrait mode.
P.L. has a nice solution in this topic: Present and instantly dismiss an empty modal view controller which allows only portrait | landscape
How to change the device orientation programmatically in iOS 6

UIScrollView shifts below navigation and status bar

I have view which contains a scrollView. When the view shows up the image appears in full screen (320x480) hiding the status and navigation bar. When I tap the screens - status and navigation bar appears on the screen. But this thing shifts the UIScrollView below the the navigation bar. I want the status and nav bar to show over my scroll view.
Like it happens in the photos app. On tapping the image the status and nav bar shows over the scrollView.
Below is how I am positioning the view
//What I have done in viewDidLoad
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
self.wantsFullScreenLayout = YES;
scrollView = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = scrollView;
Below is what I am doing on detecting a single tap
- (void)tapDetectingImageView:(TapDetectingImageView *)view gotSingleTapAtPoint:(CGPoint)tapPoint {
// single tap hide/unhide the status and nav bar
NSLog(#"got a single tap");
if (self.navigationController.navigationBarHidden == NO)
{
[self.navigationController setNavigationBarHidden:YES animated:NO];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:YES];
}
else
{
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:YES];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
}
Just a pointer for you guys -
If I use [self.view addSubview:scrollView]; I dont see this issue. But this mess up my landscape orientation code.
Anyone who can shed some light what I might be doing wrong - would be great help!
There appears to be some special handling inside UIScrollView when the navigation toolbars are toggled. As you pointed out at the end of your question, if you put the scroll view inside a containing UIView, this repositioning problem does not occur.
To fix the landscape issues, make sure you're setting the autoresizingMask on both the container view and the scroll view. I did not have to add any additional handling for landscape mode in my project.
I found the solution myself.
In my tap detecting code's else fn I add the below line in the last.
scrollView.contentOffset = CGPointMake(parentScrollView.contentOffset.x,0);
Not sure if this is the right approach. But if anyone can suggest a better approach - more than welcome!