ViewController orientation change - iphone

I'm pushing a ViewController when the iPhone changes orientation to landscape and I'm having trouble with changing the orientation of the ViewController.
I used that code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
Storage *strg = [Storage sharedStorage];
if ([strg.orient intValue] == 2)
{
[[UIApplication sharedApplication] setStatusBarOrientation:
UIInterfaceOrientationLandscapeRight];
UIScreen *screen = [UIScreen mainScreen];
CGFloat screenWidth = screen.bounds.size.width;
CGFloat screenHeight = screen.bounds.size.height;
UIView *navView = [[self navigationController] view];
navView.bounds = CGRectMake(0, 0, screenHeight, screenWidth);
navView.transform = CGAffineTransformIdentity;
navView.transform = CGAffineTransformMakeRotation(1.57079633);
navView.center = CGPointMake(screenWidth/2.0, screenHeight/2.0);
[UIView commitAnimations];
}
if ([strg.orient intValue] == 1)
{
[[UIApplication sharedApplication] setStatusBarOrientation:
UIInterfaceOrientationLandscapeLeft];
UIScreen *screen = [UIScreen mainScreen];
CGFloat screenWidth = screen.bounds.size.width;
CGFloat screenHeight = screen.bounds.size.height;
UIView *navView = [[self navigationController] view];
navView.bounds = CGRectMake(0, 0, screenHeight, screenWidth);
navView.transform = CGAffineTransformIdentity;
navView.transform = CGAffineTransformMakeRotation(4.71238898);
navView.center = CGPointMake(screenWidth/2.0, screenHeight/2.0);
[UIView commitAnimations];
}
}
The result is not consistent; sometimes it goes into the right orientation and sometimes it's upside-down.
When I go from LandcapeRight to LandscapeLeft strait away (and vise versa) it works fine, the problem is only when I go to portrait mode.
Any ideas what I'm doing wrong?

If you really are responding to device orientation change they you probably shouldn't be using setStatusBarOrientation. I think you'd be better off making your viewcontrollers rotate to the supported orientations using shouldAutorotateToInterfaceOrientation and deviceDidRotateSelector notifications.
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(deviceDidRotateSelector:) name: UIDeviceOrientationDidChangeNotification object: nil];
-(void)deviceDidRotateSelector:(NSNotification*) notification {
// respond to rotation here
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
//Return YES for supported orientations
return YES;
}

Related

Prevent some view controller in a navigation controller from rotating

I have a navigation controller which displays two view controllers at once. They are layered and the front most controller can be dragged down to reveal the controller below.
My app delegate is configured so the app will only allow interface orientations when the view in the back is being revealed, as seen below.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (self.revealNavigationController.isViewRevealed)
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
return UIInterfaceOrientationMaskPortrait;
}
Naturally this causes both the front and the back view controller to rotate when the device is rotated but I am only interested in rotating the back view controller.
Is there any way to completely disable rotation in the front most view controller?
You need to use the view controller containment APIs added in iOS5. Basically, what you need to do is remove your one view controller when you no longer want it to participate in the rotation events, and add it back when its ready again. A sample would be...
#implementation AXRotationDemoViewController
- (id)init
{
if ((self = [super init]))
{
self.oneViewController = [[AXLoggingViewController alloc] init];
self.oneViewController.interfaceOrientations = UIInterfaceOrientationMaskPortrait;
self.twoViewController = [[AXLoggingViewController alloc] init];
self.twoViewController.interfaceOrientations = UIInterfaceOrientationMaskAllButUpsideDown;
}
return self;
}
- (void)viewDidLoad
{
[self.oneViewController willMoveToParentViewController:self];
[self addChildViewController:self.oneViewController];
[self.view addSubview:self.oneViewController.view];
[self.oneViewController didMoveToParentViewController:self];
[self.twoViewController willMoveToParentViewController:self];
[self addChildViewController:self.twoViewController];
[self.view addSubview:self.twoViewController.view];
[self.twoViewController didMoveToParentViewController:self];
self.oneViewController.view.backgroundColor = [UIColor redColor];
self.twoViewController.view.backgroundColor = [UIColor greenColor];
UIButton * showBackButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[showBackButton setTitle:#"Toggle Back/Front" forState:UIControlStateNormal];
[showBackButton addTarget:self action:#selector(_toggle:) forControlEvents:UIControlEventTouchUpInside];
[showBackButton sizeToFit];
showBackButton.center = self.view.center;
[self.view addSubview:showBackButton];
}
- (void)_toggle:(id)sender
{
if ([self.childViewControllers containsObject:self.oneViewController])
{
[self.oneViewController.view removeFromSuperview];
[self.oneViewController removeFromParentViewController];
}
else
{
[self.oneViewController willMoveToParentViewController:self];
[self addChildViewController:self.oneViewController];
[self.view addSubview:self.oneViewController.view];
[self.oneViewController didMoveToParentViewController:self];
UIWindow * window = self.view.window;
UIViewController * hack = window.rootViewController;
window.rootViewController = nil;
window.rootViewController = hack;
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGRect halfScreenFrame = self.view.bounds;
halfScreenFrame.size.height /= 2;
self.oneViewController.view.frame = halfScreenFrame;
self.twoViewController.view.frame = CGRectOffset(halfScreenFrame, 0, halfScreenFrame.size.height);
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([self.childViewControllers containsObject:self.oneViewController])
return self.oneViewController.supportedInterfaceOrientations;
return self.twoViewController.supportedInterfaceOrientations;
}
#end
However you may notice, there is no official way to really tell iOS you've changed the value of supportedInterfaceOrientations, so your best bet is either a) use a hack like in my example above to force iOS to redo everything or b) don't let the user close the back view until the device is already in portrait again.
Here is the sample I put together if that doesn't explain things well enough.
Yes, take a look at the -supportedInterfaceOrientations method in UIViewController. You return a UIInterfaceOrientationMask bitmask that specifies the orientations you want that UIViewController subclass to support.
The solution suggested by axiixc is generally better so depending on your problem, you should look at his answer. However, this did not solve my problem. I had a UINavigationController in which I inserted the view of a view controller as subview at index 0. This way, the view would be in the back. I had then made the UINavigationBar draggable so that when it dragged down, it would reveal the view in the back. The front most view would then move with the navigation bar.
I did not manage to get this to work with the view controller containment API introduced iOS 5, as suggested by axiixc.
Instead, I removed the view from the navigation controller and added it directly to the UIWindow when the app was launched and I handled the rotation of this view myself and disabled rotation on all other views (that is, I disabled it on the navigation controller).
This is how I added the view in -application:didFinishLaunchingWithOptions:
self.revealViewController = [[RevealViewController alloc] init];
[self.window insertSubview:self.revealViewController.view atIndex:0];
Then, right after that I registered for UIDeviceOrientationDidChangeNotification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
My didRotate: looks like this: (this is inspired by another answer on StackOverflow which I am now unable to find again, sorry)
- (void)didRotate:(NSNotification *)notification
{
// Only rotate if the view is revealed
if (self.revealNavigationController.isViewRevealed)
{
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
[self updateForDeviceOrientation:orientation animated:YES];
}
}
I call -updateForDeviceOrientation:animated:
- (void)updateForDeviceOrientation:(UIDeviceOrientation)orientation animated:(BOOL)animated
{
CGFloat degrees = 0.0f;
switch (orientation)
{
case UIDeviceOrientationLandscapeLeft:
degrees = 90.0f;
break;
case UIDeviceOrientationLandscapeRight:
degrees = -90.0f;
break;
case UIDeviceOrientationPortrait:
degrees = 0.0f;
break;
default:
break;
}
CGFloat duration = (animated) ? [UIApplication sharedApplication].statusBarOrientationAnimationDuration : 0.0f;
__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:duration animations:^{
// Rotate view
weakSelf.revealViewController.view.transform = CGAffineTransformIdentity;
weakSelf.revealViewController.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
// Resize view for rotation
CGFloat width, height;
if (UIDeviceOrientationIsLandscape(orientation))
{
width = MAX(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
height = MIN(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
}
else
{
width = MIN(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
height = MAX(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
}
CGSize newSize = CGSizeMake(width, height);
CGRect viewBounds = weakSelf.revealViewController.view.bounds;
viewBounds.size = newSize;
weakSelf.revealViewController.view.bounds = viewBounds;
CGRect viewFrame = weakSelf.revealViewController.view.frame;
viewFrame.size = newSize;
weakSelf.revealViewController.view.bounds = viewFrame;
}];
}

Rotating the screen from Portrait to Landscape

I use following code I found in the web to rotate the screen to landscape mode. I don’t understand what they suppose to do. Specially the bounds it is setting. Can someone give some explanation what it is doing?
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait)
{
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
UIScreen *screen = [UIScreen mainScreen];
CGRect newBounds = CGRectMake(0, 0, screen.bounds.size.height, screen.bounds.size.width - statusBarFrame.size.height);
self.navigationController.view.bounds = newBounds;
self.navigationController.view.center = CGPointMake(newBounds.size.height / 2.0, newBounds.size.width / 2.0);
self.navigationController.view.transform = CGAffineTransformConcat(self.navigationController.view.transform, CGAffineTransformMakeRotation(degreesToRadian(90)));
self.navigationController.view.center = window.center;
}
Your rootView's size used to be (320, 480) for example, after rotating, you should set it to (480, 320) in order to fit the screen in landscape mode, that's why you need to change the bounds of your view.
Set the transform is making the view actually rotate 90 degrees.
UIKit is doing the similar things when automatically rotate for you.
Hello Janaka,
You will try this code.
Take a look at the function `shouldAutorotateToInterfaceOrientation`: in the UIViewController class. This function returns YES if the orientations is supported by your UIView. If you return YES only to the landscape orientation, then the iPhone will automatically be put in that orientation.
The following code should do it. Put it in the UIViewController that controls the view that you want to put in landscape mode.
Use this Method.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscape);
}
Try this link its definitely help you
this
#define degreesToRadians(x) (M_PI * x / 180.0)
- (void)viewWillAppear:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
CGRect newBounds = CGRectMake(0, 0, 480, 320);
self.navigationController.view.bounds = newBounds;
self.navigationController.view.center = CGPointMake(newBounds.size.height / 2.0, newBounds.size.width / 2.0);
self.navigationController.view.transform = CGAffineTransformMakeRotation(degreesToRadians(90));
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
self.navigationController.view.transform = CGAffineTransformIdentity;
self.navigationController.view.transform = CGAffineTransformMakeRotation(degreesToRadians(0));
self.navigationController.view.bounds = CGRectMake(0.0, 0.0, 320.0, 480.0);
[super viewWillDisappear:animated];
}

Animated UIPickerView not rotating in landscape mode

I have a UIPickerView that animates up and down in a UITableViewController. I adapted an example from apple (DateCell). The UIPickerView is created programmatically, no nib files.
In Portrait mode everything looks nice. When I rotate the simulator (I not testing on the device), the UITableView rotates well, but the picker remains were it was. I read tons of threads about that topic and many developers seem to have problems with pickers behaving weird in landscape mode but at least their pickers rotate. I made a subclass of UIPickerView as described in this link: http://www.llamagraphics.com/developer/using-uidatepicker-landscape-mode, but it didn't help for the rotation issue.
I tried to rotate the picker with a transform but it looked very weird, like broken.
I'm suspecting that the problem is that I'm using the picker inside of a UITableViewController, and so I add it as a subview of self.view.window. If I try to add the picker as a subview of self.view, only a white frame (without any picker) appears.
Any suggestions?
The initialization code in the UITableViewController subclass:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.pickerView.superview == nil)
{
//Initialization code
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect initFrame = orientation == UIInterfaceOrientationPortrait ? CGRectMake(0, 0, 320, 200) : CGRectMake(0, 0, 480, 160);
self.pickerView = [[RotatingUIPickerView alloc] initWithFrame:initFrame];
[self.pickerView setDelegate:self];
[self.pickerView setShowsSelectionIndicator:YES];
[self.pickerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];
[self.view.window addSubview:self.pickerView];
// compute the start frame
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGSize pickerSize = [self.pickerView sizeThatFits:CGSizeZero];
CGRect startRect = CGRectMake(0.0,
screenRect.origin.y + screenRect.size.height,
pickerSize.width, pickerSize.height);
[self.pickerView setFrame:startRect];
// compute the end frame
CGRect pickerRect = CGRectMake(0.0,
screenRect.origin.y + screenRect.size.height - pickerSize.height,
pickerSize.width,
pickerSize.height);
// start the slide up animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
// we need to perform some post operations after the animation is complete
[UIView setAnimationDelegate:self];
[self.pickerView setFrame:pickerRect];
[UIView commitAnimations];
}
}
Implementation of UIPicker subclass as described on the link above:
- (id)initWithFrame:(CGRect)frame
{
if (self == [super initWithFrame:frame])
{
for (UIView * subview in self.subviews)
{
[subview setFrame:self.bounds];
}
}
return self;
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
if (self == [super initWithCoder: aDecoder])
{
for (UIView * subview in self.subviews)
{
[subview setFrame:self.bounds];
}
}
return self;
}
By executing:
[self.view.window addSubview:self.pickerView];
you are adding pickerView as a subview of your UIWindow. I don't know how the main UIWindow can be rotated, if it can be.
When you are adding pickerView to the view
[self.view addSubview:self.pickerView];
you get problems due to the view being a UITableView.
What I suggest is adding pickerView to some intermediate UIView, added as subview to UIWindow and to which you add the UITableView. This intermediate UIView would also have a UIViewController associated to it so that shouldAutorotateToInterfaceOrientation can return the proper value to have auto rotation working.

Automatically Sizing UIView after Adding to Window

Note: This may be a duplicate of Subview Doesnt AutoSize When Added to Root View Controller
I have an iPad app that switches between different views in its main window. The view-switching code looks like this:
- (void)switchToViewController:(UIViewController*)viewController {
if (currentViewController != viewController) {
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
[window addSubview:viewController.view];
}
}
The problem is that when the new view (a UISplitView) appears in landscape orientation, it is not sized to fill the entire window. There is a large empty black space on the right. It looks like the view is only 768 pixels wide, rather than the 1024-pixel width of the landscape window.
If I rotate the device to portrait and then back to landscape, the view sizes itself properly.
If the device is in portrait orientation, everything works fine. The UISplitView also gets sized properly if it is the first view I show. The problem only occurs if I switch to it after another view has been shown, in landscape.
So, is there some way to force iPhone OS to resize the view after it has been added to the window?
I've tried calling sizeToFit, and setNeedsLayout. I've also tried setting the view's bounds to the window's bounds, and I've tried setting the frame to match the previous view's frame.
This is absolutely possible! :-)
You can check out my repo here:
https://github.com/hfossli/AGWindowView
It will automatically deal with any rotation and framechanges so you won't have to worry about that.
If you like to worry about that then you can just cut and paste the most important parts
# 1 Add view to window
[[UIApplication sharedApplication] keyWindow] addSubview:aView];
# 2 Add listener and update view
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
Remember to remove notification listening
[[NSNotificationCenter defaultCenter] removeObserver:self];
# 3 Do the math
- (void)statusBarFrameOrOrientationChanged:(NSNotification *)notification
{
/*
This notification is most likely triggered inside an animation block,
therefore no animation is needed to perform this nice transition.
*/
[self rotateAccordingToStatusBarOrientationAndSupportedOrientations];
}
- (void)rotateAccordingToStatusBarOrientationAndSupportedOrientations
{
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
CGFloat angle = UIInterfaceOrientationAngleOfOrientation(statusBarOrientation);
CGFloat statusBarHeight = [[self class] getStatusBarHeight];
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
CGRect frame = [[self class] rectInWindowBounds:self.window.bounds statusBarOrientation:statusBarOrientation statusBarHeight:statusBarHeight];
[self setIfNotEqualTransform:transform frame:frame];
}
- (void)setIfNotEqualTransform:(CGAffineTransform)transform frame:(CGRect)frame
{
if(!CGAffineTransformEqualToTransform(self.transform, transform))
{
self.transform = transform;
}
if(!CGRectEqualToRect(self.frame, frame))
{
self.frame = frame;
}
}
+ (CGFloat)getStatusBarHeight
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(UIInterfaceOrientationIsLandscape(orientation))
{
return [UIApplication sharedApplication].statusBarFrame.size.width;
}
else
{
return [UIApplication sharedApplication].statusBarFrame.size.height;
}
}
+ (CGRect)rectInWindowBounds:(CGRect)windowBounds statusBarOrientation:(UIInterfaceOrientation)statusBarOrientation statusBarHeight:(CGFloat)statusBarHeight
{
CGRect frame = windowBounds;
frame.origin.x += statusBarOrientation == UIInterfaceOrientationLandscapeLeft ? statusBarHeight : 0;
frame.origin.y += statusBarOrientation == UIInterfaceOrientationPortrait ? statusBarHeight : 0;
frame.size.width -= UIInterfaceOrientationIsLandscape(statusBarOrientation) ? statusBarHeight : 0;
frame.size.height -= UIInterfaceOrientationIsPortrait(statusBarOrientation) ? statusBarHeight : 0;
return frame;
}
CGFloat UIInterfaceOrientationAngleOfOrientation(UIInterfaceOrientation orientation)
{
CGFloat angle;
switch (orientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = -M_PI_2;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI_2;
break;
default:
angle = 0.0;
break;
}
return angle;
}
UIInterfaceOrientationMask UIInterfaceOrientationMaskFromOrientation(UIInterfaceOrientation orientation)
{
return 1 << orientation;
}
Good luck!
This works, but it seems a little hacky:
- (void)switchToViewController:(UIViewController *)viewController {
if (viewController != currentViewController) {
UIInterfaceOrientation orientation = currentViewController.interfaceOrientation;
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
UIView *view = viewController.view;
// Set appropriate view frame (it won't be autosized by addSubview:)
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
if (UIInterfaceOrientationIsLandscape(orientation)) {
// Need to flip the X-Y coordinates for landscape
view.frame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width);
}
else {
view.frame = appFrame;
}
[window addSubview:view];
}
}
The window may include other UI elements besides your view. The 20 pixel difference in your example is the height of the status bar.
[[UIApplication sharedApplication] statusBarFrame].height;
Neither the window nor screen rotate. Getting their frames and using them for a rotated view will only work if you have switched the height and width.
If you are using a UIViewController, try returning YES from this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
I got the same problem, but i fixed it with this lines of code:
- (void)changeRow:(NSNotification *)notification {
[window addSubview:new.view];
[old.view removeFromSuperview];
[new.view removeFromSuperview];
[window addSubview:new.view];
}
You must add the new view, then remove the old and the new and then add the new view. I don't know why, but that works.
Fossli's answer is correct for iPad. However, I have a universal app that I needed to support. Therefore some adjustments are necessary.
Add the following to AppDelegate.h
#property (strong, nonatomic) UIImageView *imageView;
Add the following to AppDelegate.m
#synthesize imageView;
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (! (UIInterfaceOrientationIsLandscape(deviceOrientation) ||
UIInterfaceOrientationIsPortrait(deviceOrientation)))
{
// May be "UIInterfaceOrientationUnknown" which does not appear to be a defined value anywhere.
return;
}
[imageView setImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
/*
iOS Image Sizes
iPhone/iPod Portrait 320 x 480 (640 x 960 #2x)
iPad Portrait 768 x 1004 (1536 x 2008 #2x)
Landscape 1024 x 748 (2048 x 1496 #2x)
iPad window bounds in both orientations 768 x 1024 (needs manual swap in landscape)
iPhone window bounds in both orientations 320 x 480 (needs manual swap in landscape)
Note the size variations between the required default launch image sizes and
the size of the window bounds.
iPhone/iPod only requires rotations.
iPad needs origin or size adjustments depending on orientation.
*/
CGFloat angle = 0.0;
CGRect newFrame = [[self window] bounds];
// How to get size of status bar
// Size of status bar gets all wonky on rotations so just set it manually
// CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
CGSize statusBarSize = CGSizeMake(20.0, 20.0);
if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
angle = M_PI;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.height -= statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft)
{
angle = - M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.x += statusBarSize.height;
newFrame.size.width += statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeRight)
{
angle = M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.width -= statusBarSize.height;
}
}
else
{
angle = 0.0;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.y += statusBarSize.height;
newFrame.size.height -= statusBarSize.height;
}
}
imageView.transform = CGAffineTransformMakeRotation(angle);
imageView.frame = newFrame;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Add background image to window with orientation changes so that it is visible in all views.
// A listener is added since subviews do not receive orientation changes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object: nil];
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
[[self window] addSubview:imageView];
return YES;
}
Add the following to Utility.h
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation;
Add the following to Utility.m
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation
{
NSString *imageName = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~ipad.png";
}
else
{
imageName = #"Default-Portrait~ipad.png";
}
}
else
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~iphone.png";
}
else
{
imageName = #"Default.png";
}
}
return imageName;
}
Windows of iOS7 have different behaviors with windows of iOS8/9.
Keyboard window of iOS7 and all windows of iOS8/9 always have correct orientation & size. So you can observer the size change events and update the frame of your view.
But other windows of iOS7 always keep the portrait orientation and size. You need update transform of your view after rotation.
You need to observer UIApplicationWillChangeStatusBarOrientationNotification and update size of your UIView like this:
#interface MyView : UIView
#end
#implementation MyView
- (instancetype)init
{
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeOrientationHandler:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)updateTransformWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
CGFloat offset = (height - width) / 2;
CGAffineTransform transform;
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIInterfaceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(-M_PI);
break;
default:
transform = CGAffineTransformIdentity;
break;
}
self.transform = transform;
self.frame = CGRectMake(0, 0, width, height);
}
- (void)updateFrameWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
self.frame = CGRectMake(0, 0, height, width);
break;
default:
self.frame = CGRectMake(0, 0, width, height);
break;
}
}
- (void)updateWithOrientation:(UIInterfaceOrientation)orientation
{
BOOL isIos7 = [[UIDevice currentDevice].systemVersion floatValue] < 8.0;
BOOL isKeyboardWindow = [self.window isKindOfClass:NSClassFromString(#"UITextEffectsWindow")];
if (isIos7 == YES && isKeyboardWindow == NO) {
[self updateTransformWithOrientation:orientation];
} else {
[self updateFrameWithOrientation:orientation];
}
}
- (void)changeOrientationHandler:(NSNotification *)notification
{
[UIView animateWithDuration:0.25 animations:^{
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
[self updateWithOrientation:orientation];
}];
}
#end

Iphone: Is it possible to hide the TabBar? (Pre-iOS 8)

I have an application that uses a UITabBarController to switch between modes. When in a certain mode, I'd like to hide the tab bar until the steps of that mode have been completed. Note that I'm not using a navigation controller so I can't use the setHidesBottomBarWhenPushed method on the navigation controller to hide the tab bar.
Prior to iOS 8, When I attempt to hide the tarbar using:
self.tabBarController.tabBar.hidden = YES
the tab bar goes away, but it leaves a 50 pixel blank area at the bottom of the screen where the tab bar used to be. I can't seem to figure out how to fill that area. Anything in the UI that is in that area is clipped and cannot be seen.
Any ideas if this is even possible? I'd really like to stay away from the navigation controller.
Here's my code for that:
This is, of course, mucking with the goings on in the controller's view hierarchy. It could change/break. This uses defined APIs, so Apple won't care, but they won't care about breaking your code, either.
- (void)hideTabBar {
UITabBar *tabBar = self.tabBarController.tabBar;
UIView *parent = tabBar.superview; // UILayoutContainerView
UIView *content = [parent.subviews objectAtIndex:0]; // UITransitionView
UIView *window = parent.superview;
[UIView animateWithDuration:0.5
animations:^{
CGRect tabFrame = tabBar.frame;
tabFrame.origin.y = CGRectGetMaxY(window.bounds);
tabBar.frame = tabFrame;
content.frame = window.bounds;
}];
// 1
}
- (void)showTabBar {
UITabBar *tabBar = self.tabBarController.tabBar;
UIView *parent = tabBar.superview; // UILayoutContainerView
UIView *content = [parent.subviews objectAtIndex:0]; // UITransitionView
UIView *window = parent.superview;
[UIView animateWithDuration:0.5
animations:^{
CGRect tabFrame = tabBar.frame;
tabFrame.origin.y = CGRectGetMaxY(window.bounds) - CGRectGetHeight(tabBar.frame);
tabBar.frame = tabFrame;
CGRect contentFrame = content.frame;
contentFrame.size.height -= tabFrame.size.height;
}];
// 2
}
Edit:
An anonymous user has suggested the following addition for 7.0 (i have not tested this, and could not say whether it is a workaround or an ideal implementation):
// 1. To Hide the black line in IOS7 only, this extra bit is required
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[self.tabBarController.tabBar setTranslucent:YES];
}
// 2. For IOS 7 only
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[self.tabBarController.tabBar setTranslucent:NO];
}
Edit: Entirely untested in 8.x and likely lacking in some layouts.
Like Steve, I haven't found a clean way to do this (even though Apple Photopicker does something similar). Here is what I have done:
if (systemAction)
{
// Reveal tab bar back
CGRect bounds = [[UIScreen mainScreen] bounds];
CGRect tabBarFrame = self.tabBarController.tabBar.frame;
self.tabBarController.view.frame = CGRectMake(0,0,bounds.size.width,bounds.size.height);
self.toolBar.hidden = YES;
systemAction = NO;
}
else
{
//hide tab bar
CGRect bounds = [[UIScreen mainScreen] bounds];
CGRect tabBarFrame = self.tabBarController.tabBar.frame;
CGRect navigationBarFrame = self.navigationController.navigationBar.frame;
self.tabBarController.view.frame = CGRectMake(0,0,bounds.size.width,bounds.size.height+tabBarFrame.size.height);
self.toolBar.hidden = NO;
CGRect frame = self.toolBar.frame;
frame.origin.y = bounds.size.height - frame.size.height - navigationBarFrame.size.height;
self.toolBar.frame = frame;
systemAction = YES;
}
What it is doing is pushing the view down so I can display a toolbar (and not hiding it). Obviously this is for only the 'root view' of a tabbar + navigation controller. For any subsequent views you can set the 'hidesBottomBarWhenPushed' on the viewcontroller you are pushing.
I tried a number of the solutions above, but no joy in iOS 8. I find that setting in viewWillAppear the following works for me. Should work in iOS 7 as the extendedLayoutIncludesOpaqueBars was introduced then.
self.extendedLayoutIncludesOpaqueBars = true
self.tabBarController?.tabBar.isHidden = true
self.tabBarController?.tabBar.isOpaque = true
and if you need to turn tabBars on again when you leave to use the following in viewWillDisappear.
self.tabBarController?.tabBar.isHidden = false
self.tabBarController?.tabBar.isOpaque = false
I use this to allow a return from a transition to keep the TabBar hidden. Not used it in a button action but if like me you find nothing above now works, this could be the basis of a programmable solution.
It's a bit late in the day, but of all the answers to the question that I've trawled through this afternoon, this is the one that worked best for me.
How to hide uitabbarcontroller
// Method call
[self hideTabBar:self.tabBarController];
// Method implementations
- (void)hideTabBar:(UITabBarController *) tabbarcontroller
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in tabbarcontroller.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 480, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 480)];
}
}
[UIView commitAnimations];
}
- (void)showTabBar:(UITabBarController *) tabbarcontroller
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in tabbarcontroller.view.subviews)
{
NSLog(#"%#", view);
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 431, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 431)];
}
}
[UIView commitAnimations];
}
I use only this single line to achieve this. I use prepareForSegue method before showing the view controller having the tab bar.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"showLogin"]){
[segue.destinationViewController setHidesBottomBarWhenPushed:YES];
}
}
I had worked on almost the same case, actually used the code from http://www.developers-life.com/hide-uitabbarcontrolleruitabbar-with-animation.html and made it better according to my needs, this might help others too.
I am using a UISplitViewController as the root view controller and its detail portion is a UITabBarController, I had to hide the tabbar in portrait mode:
// In UITabBarController's custom implementation add following method,
// this method is all that will do the trick, just call this method
// whenever tabbar needs to be hidden/shown
- (void) hidetabbar:(NSNumber*)isHidden {
UITabBarController *tabBarController=self;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
CGRect tabbarFrame=CGRectZero;
for(UIView *theView in tabBarController.view.subviews) {
//NSLog(#"%#", view);
if([theView isKindOfClass:[UITabBar class]]) {
tabbarFrame=theView.frame;
if ([isHidden boolValue]) {
tabbarFrame=CGRectMake(tabbarFrame.origin.x,
tabBarController.view.frame.size.height,
tabbarFrame.size.width,
tabbarFrame.size.height);
} else {
tabbarFrame=CGRectMake(tabbarFrame.origin.x,
tabBarController.view.frame.size.height - tabbarFrame.size.height,
tabbarFrame.size.width,
tabbarFrame.size.height);
}
theView.frame=tabbarFrame;
break;
}
}
for(UIView *theView in tabBarController.view.subviews) {
if(![theView isKindOfClass:[UITabBar class]]) {
CGRect theViewFrame=theView.frame;
if ([isHidden boolValue]) {
theViewFrame=CGRectMake(theViewFrame.origin.x,
theViewFrame.origin.y,
theViewFrame.size.width,
theViewFrame.size.height + tabbarFrame.size.height);
} else {
theViewFrame=CGRectMake(theViewFrame.origin.x,
theViewFrame.origin.y,
theViewFrame.size.width,
theViewFrame.size.height - tabbarFrame.size.height);
}
theView.frame=theViewFrame;
}
}
[UIView commitAnimations];
}
I used following code to call the hidetabbar: method
//In my UISplitViewController's custom implementation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
#synchronized(self){
//change the self.splitDetailController to your UITabBarController's object
[self.splitDetailController
performSelector:#selector(hidetabbar:)
withObject:[NSNumber numberWithBool:UIInterfaceOrientationIsLandscape(interfaceOrientation)]
afterDelay:0.5];
}
return YES;
}
I tested this code to work in simulator only, let me know if it works on device too ;-)
Do you have the autoResizingMask set on the sub view?
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Something like that should do the trick and allow the view sitting atop the stack to re-size.
The obvious solution, keeping your original architecture, would have been to present that view modally:
- (void)tabBarController:(UITabBarController *)tb
didSelectViewController:(UIViewController *)vc {
if (tb.selectedIndex == MODALONE) {
UIViewController* mod =
[[UIViewController alloc] initWithNibName: #"ModalView"
bundle: nil];
[tb presentModalViewController:mod animated:NO];
[mod release];
}
}
The view now covers the entire screen (except for the status bar is there is one) including the tab bar, so it looks as if the tab bar has gone away in response to the user pressing that tab bar item.
autoresizing mask has an enumeration. Try to set all the options and check if autoresize subviews option is checked in parent view
You can create Tabbar Category and show/Hide easily. and you can access full view.
create category #import "UITabBarController+HideTabBar.h"
#implementation UITabBarController (HideTabBar)
- (void)hideTabBarAnimated:(BOOL)animated
{
CGRect statusbarFrame = [UIApplication sharedApplication].statusBarFrame;
CGRect tabBarControllerFrame = self.view.frame;
if (statusbarFrame.size.height>20)
{
tabBarControllerFrame.size.height = screenSize.size.height + self.tabBar.frame.size.height - 20.0;
}
else
{
tabBarControllerFrame.size.height = screenSize.size.height + self.tabBar.frame.size.height ;
}
if (animated) {
[UIView animateWithDuration:0.2 animations:^{
[self.view setFrame:tabBarControllerFrame];
} completion:^(BOOL finished) {
}];
}
else
[self.view setFrame:tabBarControllerFrame];
}
- (void)showTabBarAnimated:(BOOL)animated {
CGRect statusbarFrame = [UIApplication sharedApplication].statusBarFrame;
CGRect tabBarControllerFrame = self.view.frame;
if (statusbarFrame.size.height>20)
{
tabBarControllerFrame.size.height = screenSize.size.height - 20.0;
}
else
{
tabBarControllerFrame.size.height = screenSize.size.height ;
}
if (animated) {
[UIView animateWithDuration:0.2 animations:^{
[self.view setFrame:tabBarControllerFrame];
} completion:^(BOOL finished) {
}];
}
else
[self.view setFrame:tabBarControllerFrame];
}
#end
Note : use statusbarFrame is used when hotspot or call is ON so tabbar would not cut down.
Now Import category in which you class you want to use methods and just call below methods to hide or show tabbar.
[self.tabBarController hideTabBarAnimated:YES];
[self.tabBarController showTabBarAnimated:YES];
Hope this Helps.
Hope this works.
#interface UITabBarController (Additions)
-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;
#end
#implementation UITabBarController (Additions)
-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated
{
if (animated)
{
[UIView beginAnimations:nil context:nil];
}
if (hidden)
{
self.tabBar.frame = CGRectMake(self.tabBar.frame.origin.x, self.tabBar.superview.frame.size.height, self.tabBar.bounds.size.width, self.tabBar.bounds.size.height);
}
else
{
self.tabBar.frame = CGRectMake(self.tabBar.frame.origin.x, self.tabBar.superview.frame.size.height - self.tabBar.frame.size.height + 10, self.tabBar.bounds.size.width, self.tabBar.bounds.size.height);
}
if (animated)
{
[UIView commitAnimations];
}
}
Here is my solution (my tab view controller is inside navigation controller for good measure)... So I have subclassed UITabBarController and did this... exposing -setTabBarHidden: method
- (void)setTabBarHidden:(BOOL)hidden {
_tabBarHidden = hidden;
[UIView performWithoutAnimation:^{
[self adjustViews];
}];
}
- (void)adjustViews {
if ( _tabBarHidden ) {
CGRect f = self.tabBar.frame;
// move tab bar offscreen
f.origin.y = CGRectGetMaxY(self.view.frame);
self.tabBar.frame = f;
// adjust current view frame
self.selectedViewController.view.frame = self.view.frame;
} else {
CGRect f = self.tabBar.frame;
// move tab bar on screen
f.origin.y = CGRectGetMaxY(self.view.frame) - (CGRectGetMaxY(self.tabBar.bounds) + CGRectGetMaxY(self.navigationController.navigationBar.frame));
self.tabBar.frame = f;
// adjust current view frame
f = self.view.bounds;
f.size.height -= CGRectGetMaxY(self.tabBar.bounds);
self.selectedViewController.view.frame = f;
}
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[UIView performWithoutAnimation:^{
[self adjustViews];
}];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[UIView performWithoutAnimation:^{
[self adjustViews];
}];
}
put the statement in the init method of the UIViewController
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.hidesBottomBarWhenPushed = true
setupDependencyConfigurator()
}
See this thread:
Show/Hide TabBarController in iphone
In summary, you can see an example of this behavior in this sample code:
http://developer.apple.com/iphone/library/samplecode/TheElements/index.html
Why are you not using a navigation controller. It's a lot easier to hide the nav bar than the tab bar...
Just made the following code in Monotouch inside a subclass of UITabBarController:
public void ShowTabBar()
{
UIView.BeginAnimations("Anim");
UIView.SetAnimationDuration(0.25f);
this.View.Subviews[0].Frame = new RectangleF(0f, 0f, 320f, 431f);
this.TabBar.Frame = new RectangleF(0f, 431f, 320f, 49f);
this.TabBar.Hidden = false;
UIView.CommitAnimations();
}
public void HideTabBar()
{
UIView.BeginAnimations("Anim");
UIView.SetAnimationDuration(0.25f);
this.View.Subviews[0].Frame = new RectangleF(0f, 0f, 320f, 480f);
this.TabBar.Frame = new RectangleF(0f, 481f, 320f, 510f);
this.TabBar.Hidden = true;
UIView.CommitAnimations();
}