Stop transitionFormViewController from animating viewWillAppear - iphone

I am trying to put together a simple container view controller that will allow me to present child view controllers from any direction (from the right like the standard navigation controller, put also from the top, bottom and left.
Developping for iOS 5, I use the parent/child view controller relationships and use a transitionFromViewController with a custom animation block to slide in the views.
All messages (viewWillAppear, etc.) are all passed on correctly to the child view controllers, but the problem is that for example viewWillAppear seems to be called from inside an animation loop (i.e. when I set animatable properties -like backgroundColor, or frame of subviews- of the view of the child view controller, they are actually animated). I do not want this behaviour, as I would like to use the viewWillAppear methods to perform initialization before showing the view controllers. I do not want this code to be animated.
Here is a simplified version of my code:
#implementation ContainerViewController
#synthesize currentViewController;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
self.currentViewController = [[SimpleViewController alloc] init];
[self addChildViewController:self.currentViewController];
self.currentViewController.view.frame = self.view.bounds;
[self.view addSubview:self.currentViewController.view];
}
- (void) slideViewController {
UIViewController* previousViewController = self.currentViewController;
UIViewController* viewController = [[SimpleViewController alloc] init];
viewController.view.frame = CGRectOffset(self.view.bounds, self.view.bounds.size.width, 0);
[self addChildViewController:viewController];
[previousViewController willMoveToParentViewController:nil];
[self transitionFromViewController:previousViewController toViewController:viewController duration:1.0 options:UIViewAnimationOptionTransitionNone animations:^{
viewController.view.frame = self.view.bounds;
} completion:^(BOOL finished) {
[previousViewController removeFromParentViewController];
[viewController didMoveToParentViewController:self];
}];
self.currentViewController = viewController;
}
- (void) loop {
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self slideViewController];
[self loop];
});
}
- (void)viewDidAppear:(BOOL)animated {
[self loop];
}
#end
#implementation SimpleViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)viewWillAppear:(BOOL)animated {
CGFloat red = (arc4random()%256)/255.0;
CGFloat green = (arc4random()%256)/255.0;
CGFloat blue = (arc4random()%256)/255.0;
self.view.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1];
}
#end
If you try to run this code, you will see a new view controller slide from the right. The problem is that the backgroundColor of the view of that view controller gets animated from white to a random color (see viewWillAppear method of SimpleViewController). I would like that the backgroundColor of the view would be set immediately without being animated.

I found a workaround, by disabling the animation of properties in the viewWillAppear method (see Disabling implicit animations in -[CALayer setNeedsDisplayInRect:] thread for details)
The code can be wrapped inside a CATransaction to commit changes without animation.
- (void)viewWillAppear:(BOOL)animated {
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
CGFloat red = (arc4random()%256)/255.0;
CGFloat green = (arc4random()%256)/255.0;
CGFloat blue = (arc4random()%256)/255.0;
self.view.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1];
[CATransaction commit];
}
However I wonder if anybody else has run into this problem and if there is a better method to solve it?

After thinking about it some more, I agree with your comment, it would be better to keep all the logic for the child controller in its class. So, override init (or maybe viewDidLoad would work), in your child controller, and set any attributes you want there -- this should get things set up before your animations happen.

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;
}];
}

UIScrollView - Prevent UIImageView as subview of UIScrollView from being dragged off screen

I have a UIImageView element as a subview of my main UIScrollView element.
I want the Image to fill out the whole screen at all times, but I can drag the edges a bit too far, so that the yellow "background" becomes visible.
If I lift my finger, the Image "bounces" back and fills out the screen correctly.
I want to prevent this dragging of the image off screen.
I do however want the image to bounce back once I drag it out of the "safety area".
This is my ScrollView initialization:
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:CGRectMake(0, 0, frame.size.height, frame.size.width)];
if (self) {
[self initImageValues];
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
self.bouncesZoom = YES;
self.decelerationRate = UIScrollViewDecelerationRateFast;
self.delegate = self;
self.backgroundColor = [UIColor yellowColor];
self.minimumZoomScale = 1.0;
self.maximumZoomScale = 2.0;
[self setCanCancelContentTouches:YES];
self.clipsToBounds = YES;
// Load Image
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
[self addSubview:imageView];
[self setContentSize: CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
// Set bigger "bounce" zone (safety area)
self.contentInset=UIEdgeInsetsMake(-SAFETY_ZONE,-SAFETY_ZONE,-SAFETY_ZONE,-SAFETY_ZONE);
self.scrollEnabled = YES;
}
return self;}
Use these delegate methods:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
Then read the offset and return if it's out of the "safety area".
Could be that you need another delegate method from the UIScrollView though, but a workaround like this should fix your problem :)
try setting bounces on the scrollview:
self.bounces = NO;
TRY this:
**
self.scrollView.maximumZoomScale = 1.0;
self.scrollView.minimumZoomScale = 1.0;
**
Try setting its bounces property to NO.

Present Modal View with Animation Effect

In my iPhone App I need to display a modal view with transparent background and it should appear with animation like it is appearing from center of view and its size is increasing.
similar like "drawing something" iPhone App when we click on settings button.
How do I do this?
You can do one of 4 following transition styles:
viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
viewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:viewController animated:YES];
If you want something that is not included in these defaults you are going to have to build your own custom animation for presenting the modal view. Like the following but obviously for the style you want.
UIModalTransitionStyle horizontal movement
Let's say you have a viewController thats called aScoreSheet that you want to present. Try to define this method in the view controller that's going to do the presenting.
-(void) presentTransparentModalViewController: (ScoreSheet *) aViewController
{
scoreSheet = aViewController;
UIView *view = aViewController.view;
view.opaque = NO;
[view.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIView *each = obj;
each.opaque = NO;
}];
[self.view addSubview:view];
view.center = CGPointMake(160, 800); //for iPhone
[UIView animateWithDuration:0.9 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
view.center = CGPointMake(160, 240);
} completion:^(BOOL finished) {
self.view.userInteractionEnabled=YES;
}];
}
and then to dismiss the controller:
-(void) dismissTransparentModalViewControllerAnimated:(BOOL) animated{
if (animated) {
[UIView animateWithDuration:0.4
animations:^{
scoreSheet.view.center = CGPointMake(scoreSheet.view.center.x, scoreSheet.view.center.y + 480);
} completion:^(BOOL finished) {
[scoreSheet.view removeFromSuperview];
scoreSheet = nil;
}];
}
}
Not a full answer, but maybe you can take a look at this open source library:
https://github.com/Split82/HMGLTransitions
It has some custom modal transitions, maybe not exactly the one you are looking for, but you can easily add your transition by subclassing HMGLTransition.
Hope this helps

presentModalViewController in CGRect

Is it possible to present a modal view controller in such a way that the modal view is confined to the space included in a CGRect?
If not, please explain how to replicate the cross-disolve modal view transition between two views.
Thanks.
To cross-dissolve to a regular view controller, you can set it's modalTransitionStyle to UIModalTransitionStyleCrossDissolve then present it modally.
To perform a cross-dissolve between some pair of subviews (confined to their frame CGRects), you can use this UIView method:
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion.
Here's how you might use that in code:
#interface ViewController ()
#property(strong,nonatomic) UIView *redView;
#property(strong,nonatomic) UIView *blueView;
#end
#implementation ViewController
#synthesize redView=_redView;
#synthesize blueView=_blueView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.redView = [[UIView alloc] initWithFrame:CGRectMake(40.0, 40.0, 240.0, 100.0)];
self.redView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.redView];
self.blueView = [[UIView alloc] initWithFrame:CGRectMake(40.0, 40.0, 240.0, 100.0)];
self.blueView.backgroundColor = [UIColor blueColor];
}
- (IBAction)crossDisolve:(id)sender {
UIView *fromView = (self.redView.superview)? self.redView : self.blueView;
UIView *toView = (fromView==self.redView)? self.blueView : self.redView;
[UIView transitionFromView:fromView
toView:toView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
completion:^(BOOL finished) {NSLog(#"done!");}
];
// now the fromView has been removed from the hierarchy and the toView has been added
// please note that this code depends on ARC to release objects correctly
}
The harder part of your question is the idea of making that new sub-view "modal" by which I'm guessing you mean that covers only part of the display but takes input focus exclusively. The nearest thing to that in the SDK is UIAlertView.
How about you just use UIView animations:
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(x,y,w,h)];
[view setAlpha:0];
[self.view addSubView:view];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[view setAlpha:1];
[UIView commitAnimations];
Voila! It fades in! :)

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();
}