11

I want to know how to implement an animation which hiding the tab bar when dragging downward, like Safari app on iOS 7. Any information from you will be appreciated.

Similar question: Show/hide UIToolbar, "match finger movement", precisely as in for example iOS7 Safari.

Community
  • 1
  • 1
zono
  • 8,366
  • 21
  • 75
  • 113

6 Answers6

18

Something like this should work. I don't know if this gives exactly the same look as the Safari app, but it's close:

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (nonatomic) CGRect originalFrame;
@end

@implementation ViewController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1000);
    self.originalFrame = self.tabBarController.tabBar.frame;
}


-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    UITabBar *tb = self.tabBarController.tabBar;
    NSInteger yOffset = scrollView.contentOffset.y;
    if (yOffset > 0) {
        tb.frame = CGRectMake(tb.frame.origin.x, self.originalFrame.origin.y + yOffset, tb.frame.size.width, tb.frame.size.height);
    }
   if (yOffset < 1) tb.frame = self.originalFrame;
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Thanks! It works! You and this site below helped me a lot. http://www.developers-life.com/hide-uitabbarcontrolleruitabbar-with-animation.html – zono Nov 17 '13 at 08:21
  • 1
    I need same effect.But if it is UITableview How can I achieve this?need to do hide and show navigation bar and toolbar – Ramz Nov 20 '14 at 05:28
  • UITableView extends UIScrollView so you can use the same method as above – SeanCAtkinson Jan 19 '15 at 16:46
  • Logically, this makes sense. But I'm not able to get it to work. The tab bar frame is always: {{0, 0}, {0, 0}}. Has anyone else run into this? – Brian Sachetta Mar 04 '15 at 14:51
  • @rdelmar not completely work like safari, while yOffset decrease after increased at that time tabbar should appear. – Pramod Tapaniya Jan 23 '16 at 09:43
  • I think this is not the way it works in Safari; specifically, doesn't the tabbar show up again only when you scroll back to the top of the scrollview? – Blaszard Apr 22 '16 at 05:18
8

The accepted answer doesn't work when you have a lot of cells in a table view -- The tab bar only shows up if you scroll all the way to the top.

I improved it like this:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1000);
    self.originalFrame = self.tabBarController.tabBar.frame;
}


-(void)scrollViewDidScroll:(UIScrollView *)scrollView{

UITabBar *tabBar = self.tabBarController.tabBar;

NSInteger yOffset = scrollView.contentOffset.y;
CGFloat yPanGesture = [scrollView.panGestureRecognizer translationInView:self.view].y;
CGFloat heightTabBar = tabBar.frame.size.height;

CGFloat tabBarOriginY = tabBar.frame.origin.y;
CGFloat frameHeight = self.view.frame.size.height;

if(yOffset>=heightTabBar)
    yOffset = heightTabBar;

// GOING UP ------------------
if(yPanGesture >= 0 && yPanGesture < heightTabBar && tabBarOriginY > frameHeight-heightTabBar){
    yOffset = heightTabBar - fabsf(yPanGesture);
}
else if(yPanGesture >= 0 && yPanGesture < heightTabBar && tabBarOriginY <= frameHeight-heightTabBar){
    yOffset = 0;
}
// GOING DOWN ------------------
else if(yPanGesture < 0 && tabBarOriginY < frameHeight){
    yOffset = fabsf(yPanGesture);
}else if(yPanGesture < 0 && tabBarOriginY >= frameHeight){
    yOffset = heightTabBar;
}
else{
    yOffset = 0;
}

if (yOffset > 0) {
    tabBar.frame = CGRectMake(tabBar.frame.origin.x, self.originalFrame.origin.y + yOffset, tabBar.frame.size.width, tabBar.frame.size.height);
}
else if (yOffset <= 0){
    tabBar.frame = self.originalFrame;
}
}

    - (void)scrollViewWillEndDragging:(UIScrollView*)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{ 

// Handle unfinished animations   
    UITabBar *tabBar = self.tabBarController.tabBar;
    CGFloat yPanGesture = [scrollView.panGestureRecognizer translationInView:self.view].y;
    CGFloat heightTabBar = tabBar.frame.size.height;
    CGFloat tabBarOriginY = tabBar.frame.origin.y;
    CGFloat frameHeight = self.view.frame.size.height;


    if(yPanGesture > 0){

        if(tabBarOriginY != frameHeight - heightTabBar) {

            [UIView animateWithDuration:0.3 animations:^(void){
                tabBar.frame = self.originalFrame;
            }];

        }

    }else if(yPanGesture < 0){

    if (tabBarOriginY != frameHeight) {
        [UIView animateWithDuration:0.3 animations:^(void){
            tabBar.frame = CGRectMake(tabBar.frame.origin.x, frameHeight, tabBar.frame.size.width, tabBar.frame.size.height);
        }];
    }

}

}
jonypz
  • 1,515
  • 1
  • 20
  • 35
3

Honestly, I tried all these solutions and they weren't robust enough for me. They would hide the tab bar when it should have been showing, or they moved the tab bar up in a strange way.

I ended up spending the whole day trying to get them to work. I finally gave up and used: https://github.com/tristanhimmelman/HidingNavigationBar.

This gave me the functionality of hiding the nav bar and tab bar (which is what I really wanted in the first place). It works extremely well and I set it up in 10 minutes.

Hope this helps someone else not lose a day of coding to this :-0.

Rob Norback
  • 6,401
  • 2
  • 34
  • 38
1

You can use a variable to keep the lastContentOffset value. The key idea is tab bar origin.y is ranging between CGRectGetMaxY(screenRect) and CGRectGetMaxY(screenRect) - CGRectGetHeight(tabBarRect).

It works well in my project.

@interface ViewController ()
    @property (nonatomic, assign) CGFloat lastContentOffsetY;
@end

@implementation ViewController
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (!self.tabBarController) {
      return;
    }

  BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden;
  CGFloat kStatusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;

if (scrollView.contentOffset.y > (scrollView.contentSize.height - scrollView.frame.size.height)
    || (scrollView.contentOffset.y < (isStatsuBarHidden ? 0 : -kStatusBarHeight))) {
    // bottom & top bouncing - don't need to update
    return;
}

CGFloat offset = scrollView.contentOffset.y - self.lastContentOffsetY;
CGRect tabBarRect = self.tabBarController.tabBar.frame;
CGRect screenRect = [UIScreen mainScreen].bounds;

if (CGRectGetMaxY(tabBarRect) == CGRectGetMaxY(screenRect) + CGRectGetHeight(tabBarRect)) {
    //view could only scroll downward
    if (offset < 0) {
        tabBarRect.origin.y += offset;
    }
} else if (CGRectGetMaxY(tabBarRect) == CGRectGetMaxY(screenRect)) {
    //view could only scroll upward
    if (offset > 0) {
        tabBarRect.origin.y += offset;
    }
} else {
    //view could scroll upward & downward
    tabBarRect.origin.y += offset;
}

//safty reset handling
if (CGRectGetMaxY(tabBarRect) > CGRectGetMaxY(screenRect) + CGRectGetHeight(tabBarRect)) {
    tabBarRect.origin.y = CGRectGetMaxY(screenRect);
} else if (CGRectGetMaxY(tabBarRect) < CGRectGetMaxY(screenRect)) {
    tabBarRect.origin.y = CGRectGetMaxY(screenRect) - CGRectGetHeight(tabBarRect); // over bouncing, set it back
}

self.tabBarController.tabBar.frame = tabBarRect;
self.lastContentOffsetY = scrollView.contentOffset.y;
}
Yang Young
  • 602
  • 5
  • 6
0

Well, while all the answers by others do neither work nor work partially, the HidingNavigationBar by @RobNorback works brilliantly, and it is in fact pretty easy to set up.

However, I have struggled a bit with the framework, and spent 2 hours fixing it, so I add my two cents for someone to not get in the same wrap.

If the number of items or rows in your collection view or table view is small, it does not hide the navbar (or tabbar or toolbar), even if the number of items or rows are large enough to go beyond the screen. You should add a bit more items or rows to make the framework correctly hide the bar.

Blaszard
  • 30,954
  • 51
  • 153
  • 233
0
static CGFloat navBarOriginY = 20.0;

Create constant for base value of navigation bar origin Y position

- (void)viewDidLoad {
[super viewDidLoad];
 self.navigationController.hidesBarsOnSwipe = true;
[self.navigationController.barHideOnSwipeGestureRecognizer addTarget:self action:@selector(swipe:)];
}

Add your custom selector to handle system swipe gesture that will fire before navBar become hidden and during hiding

- (void)swipe:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateFailed) {

If gesture state ended/canceled or failed you need entirely change frame of tabBar

    CGRect finalFrame = self.tabBarController.tabBar.frame;
    if (self.navigationController.navigationBar.frame.origin.y < 0) {
         //Tab bar will be hidden
        finalFrame.origin.y = self.maxTabBarY;
    } else {
         //Tab bar will be visible
        finalFrame.origin.y = self.minTabBarY;
    }

    [self setFrameForTabBar:finalFrame animationDuration:0.3];

} else if (recognizer.state == UIGestureRecognizerStateChanged) {

If state == changed than you need to pan your tabBar with navBar like in safari app.

    CGRect frame = self.tabBarController.tabBar.frame;
    CGFloat delta = navBarOriginY - self.navigationController.navigationBar.layer.presentationLayer.frame.origin.y;
    frame.origin.y = self.minTabBarY + delta;
    [self setFrameForTabBar:frame animationDuration:0.0];
} } }

- (void)setFrameForTabBar:(CGRect)frame animationDuration:(CGFloat)duration {
dispatch_async(dispatch_get_main_queue(), ^{
    [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
        self.tabBarController.tabBar.frame = frame;
    } completion:^(BOOL finished) {}];
});
Artem
  • 391
  • 3
  • 11