【问题标题】:Hide UINavBar and UITabBar at the same time with animation用动画同时隐藏 UINavBar 和 UITabBar
【发布时间】:2013-06-06 21:08:29
【问题描述】:

编辑:我将赏金授予约翰,因为他在他的答案中付出了很多努力,并且无论如何都会得到它,但仍然没有有效的解决方案。我仍在寻找答案,如果有人知道如何做到这一点,将不胜感激。

我想在我的应用中添加一个“最大化”按钮来隐藏导航和标签栏。导航栏和标签栏应该平滑地滑入/滑出,内部/内容视图也应该以与导航栏和标签栏相同的速度扩展和收缩。

我使用[self.navigationController setNavigationBarHidden: YES/NO animated: YES]; 作为导航栏,发现这个线程How to hide uitabbarcontroller 用于隐藏标签栏。

UITabBar 类扩展:

- (void) setTabBarHidden:(BOOL)hidden animated:(BOOL)animated {
    CGRect screenRect = [[UIScreen mainScreen] bounds];

    float screenHeight = screenRect.size.height;
    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
        screenHeight = screenRect.size.width;
    }
    if (!hidden) {
        screenHeight -= self.tabBar.frame.size.height;
    }
    [UIView animateWithDuration: (animated ? UINavigationControllerHideShowBarDuration : 0) animations: ^{
        for (UIView* each in self.view.subviews) {
            if (each == self.tabBar) {
                [each setFrame: CGRectMake(each.frame.origin.x, screenHeight, each.frame.size.width, each.frame.size.height)];
            } else {
                [each setFrame: CGRectMake(each.frame.origin.x, each.frame.origin.y, each.frame.size.width, screenHeight)];
            }
        }
    } completion: ^(BOOL finished) {
        NSLog(@"Animation finished %d", finished);
    }];
}

问题是当我同时使用这两者时(隐藏/显示导航和标签栏),它不干净。如果导航栏先出现,则锚定到底部的任何内容都会跳转(参见下面的示例),如果标签栏先出现,则顶部会跳转。

示例:我将 UIButton 放在右下角并设置其自动调整大小的掩码

resizeButton.frame = CGRectMake(self.view.bounds.size.width - 50, self.view.bounds.size.height - 100, 32, 32); // hardcoded just for testing purposes
resizeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;

但是当导航栏和标签栏被最小化时,UIButton 会在两种状态之间跳转(不会随着标签栏滑动)。但是,如果我将其更改为附加到右上角,它会与导航栏完美滑动。

有人知道怎么解决吗?


编辑: 这是迄今为止我所拥有的壁橱和最优雅的解决方案(只是试图获得一个可行的概念):

[UIView animateWithDuration: UINavigationControllerHideShowBarDuration animations: ^{
    if (self.isMaximized) {
        self.tabBarController.view.frame = CGRectMake(0, 20, screenRect.size.width, screenRect.size.height + 49 - 20);
        [self.navigationController setNavigationBarHidden:YES animated:YES];
    } else {
        self.tabBarController.view.frame = CGRectMake(0, 20, screenRect.size.width, screenRect.size.height - 20);
        [self.navigationController setNavigationBarHidden:NO animated:YES];
    }
} completion: ^(BOOL finished) {
    NSLog(@"Frame done: %@", NSStringFromCGRect(self.view.frame));
    return;
}];

关于最大化:

  • 向上滑动导航栏,同时向下滑动标签栏
  • inner/content 视图的顶部向上滑动,该视图的底部向下跳

关于最小化:

  • 向下滑动导航栏,同时向上滑动标签栏
  • 内部/内容视图的顶部正确向下滑动,但底部跳转到最终值,留下空白,然后被滑动标签栏覆盖

如果我重新排列最小化动画的顺序(因此首先调用导航栏动画),则内部/内容视图中的顶部会跳转

【问题讨论】:

    标签: objective-c cocoa-touch uitabbarcontroller core-animation


    【解决方案1】:

    我使用的解决方案应该可以消除您看到的跳跃问题。

    此解决方案源自 Carlos Oliva's github page 的一个 Objective-C 类别,虽然该代码中的版权是“保留所有权利”,但我写给他,他提供了使用许可。

    我的类别代码与他的代码略有不同。另外,在类别代码下方找到我在应用程序中使用的调用代码。

    来自 UITabBarController+HideTabBar.m

    // the self.view.frame.size.height can't be used directly in isTabBarHidden or
    // in setTabBarHidden:animated: because the value may be the rect with a transform.
    //
    // further, an attempt to use CGSizeApplyAffineTransform() doesn't work because the
    // value can produce a negative height.
    // cf. http://lists.apple.com/archives/quartz-dev/2007/Aug/msg00047.html
    //
    // the crux is that CGRects are normalized, CGSizes are not.
    
    - (BOOL)isTabBarHidden {
        CGRect viewFrame = CGRectApplyAffineTransform(self.view.frame, self.view.transform);
        CGRect tabBarFrame = self.tabBar.frame;
        return tabBarFrame.origin.y >= viewFrame.size.height;
    }
    
    
    - (void)setTabBarHidden:(BOOL)hidden {
        [self setTabBarHidden:hidden animated:NO];
    }
    
    
    - (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated {
        BOOL isHidden = self.tabBarHidden;
        if (hidden == isHidden)
            return;
        UIView* transitionView = [self.view.subviews objectAtIndex:0];
    
        if (!transitionView)
        {
    #if DEBUG
        NSLog(@"could not get the container view!");
    #endif
            return;
        }
    
        CGRect viewFrame = CGRectApplyAffineTransform(self.view.frame, self.view.transform);
        CGRect tabBarFrame = self.tabBar.frame;
        CGRect containerFrame = transitionView.frame;
        tabBarFrame.origin.y = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
        containerFrame.size.height = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
        [UIView animateWithDuration:kAnimationDuration 
                         animations:^{
                             self.tabBar.frame = tabBarFrame;
                             transitionView.frame = containerFrame;
                         }
         ];
    }
    

    来自我的 ScrollableDetailImageViewController.m

    - (void)setBarsHidden:(BOOL)hidden animated:(BOOL)animated
    {
        [self setTabBarHidden:hidden animated:animated];
        [self setStatusBarHidden:hidden animated:animated];
    
        // must be performed after hiding/showing of statusBar
        [self.navigationController setNavigationBarHidden:hidden animated:animated];
    }
    
    - (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated
    {
        id parent = self.navigationController.parentViewController;
        if ([parent respondsToSelector:@selector(isTabBarHidden)]
            && hidden != [parent isTabBarHidden]
            && [parent respondsToSelector:@selector(setTabBarHidden:animated:)])
            [parent setTabBarHidden:hidden animated:animated];
    }
    

    【讨论】:

    • 谢谢,我会试试这个。 kAnimationDuration 只是任何持续时间值吗?还有,CGRectApplyAffineTransform 有什么用?我在这里找到了文档developer.apple.com/library/mac/#documentation/graphicsimaging/…,但是当我用仿射帧 NSLog 原始帧时,我得到了相同的 CGRect
    • 我刚试过这个,我得到了同样的结果。如果导航栏先出现,顶部会跳动。如果标签栏先出现(如你所说),底部仍然很跳跃。此外,当我 NSLog 常规视图框架与仿射视图框架(不更改您的代码)时,我得到相同的值
    • 首先,kAnimationDuration 无关紧要,但对于我的代码,它被硬编码为 .2 。其次,CGRectApplyAffineTransform 也无关紧要,除非 view.transform 不是身份。最后,我很好奇您的视图是否有任何viewDidLayoutSubviews 调用,或者是否存在视图层次结构导致外部视图在标签栏隐藏的情况下调整大小,但不会同时调整内部视图的大小.
    • 我的视图控制器是UIViewController 的子类。没什么特别的。它通过initWithRootViewController 添加到UINavigationController。这些视图控制器被添加到带有setViewControllers:(NSArray*)viewControllersUITabBarController。我知道没有额外的viewDidLayoutSubviews 调用,除了任何属于“默认行为”的部分(我不使用它)
    • 所以……在查看您的代码时,只需更仔细地触摸一下……您可能会受益于对[self.navigationController setNavigationBarHidden:XX animated:YES]的调用以驻留在[UIView animateWithDuration:animations:completion:]调用之外……因为我的感觉是它试图在动画之上做动画。如果您对我包含的类别的调用做了类似的事情,那也可能是跳跃的原因。
    【解决方案2】:

    试试这个代码,如果它工作正常。我一年前写过这段代码。但是,它仍然对我有用。

    我没有使用基于块的动画。因为它是我刚接触 iOS 时写的。尝试按照自己的意愿优化自己。

    - (void) hideTabBar:(UITabBarController *) tabbarcontroller {
    
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.1];
        for(UIView *view in tabbarcontroller.view.subviews)
        {
            if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
                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)];
                }            
            }else if([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
                if([view isKindOfClass:[UITabBar class]])
                {
                    [view setFrame:CGRectMake(view.frame.origin.x, 1024, view.frame.size.width, view.frame.size.height)];
                } 
                else
                {
                    [view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 1024)];
                }
            }
        }
        [UIView commitAnimations];
    }
    
    // Method shows the bottom and top bars
    
    - (void) showTabBar:(UITabBarController *) tabbarcontroller {
    
        [self.navigationController setNavigationBarHidden:NO animated:YES];
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.1];
        for(UIView *view in tabbarcontroller.view.subviews)
        {
            if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
                if([view isKindOfClass:[UITabBar class]])
                {
                    [view setFrame:CGRectMake(view.frame.origin.x, 430, view.frame.size.width, view.frame.size.height)];
                } 
                else 
                {
                    [view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 436)];
                }            
            }else if([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
                if([view isKindOfClass:[UITabBar class]])
                {
                    [view setFrame:CGRectMake(view.frame.origin.x, 975, view.frame.size.width, view.frame.size.height)];
                } 
                else 
                {
                    [view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 980)];
                }
            }
        }
        [UIView commitAnimations]; 
    }
    

    【讨论】:

    • 不幸的是它对我不起作用:/ 不过还是谢谢
    【解决方案3】:

    试试这个

    您可以使用以下动画隐藏标签栏控制器和导航栏:-

    -(IBAction)hide:(id)sender
    {
    [self hideShowBars];
    }
    - (void) hideShowBars
    {
       CGRect rect = self.navigationController.navigationBar.frame;
       CGRect rect1 = self.tabBarController.tabBar.frame;
    
       if(self.navigationController.navigationBar.isHidden)
        {
          [self.navigationController.navigationBar setHidden:NO];
          rect.origin.y=self.view.frame.origin.y;
        }
       else
       {
          rect.origin.y=self.view.frame.origin.y-rect.size.height;
       }
    
       if(self.tabBarController.tabBar.isHidden)
       {
          [self.tabBarController.tabBar setHidden:NO];
          rect1.origin.y=self.view.frame.size.height-rect1.size.height-rect1.size.height;
       }
       else
       {
          rect1.origin.y=self.view.frame.size.height;
       }
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.50];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(hideShowBarsAnimationStopped)];
    
    self.navigationController.navigationBar.frame=rect;
    self.tabBarController.tabBar.frame = rect1;
    [UIView commitAnimations];
     }
    - (void) hideShowBarsAnimationStopped
    {
    if(self.navigationController.navigationBar.frame.origin.y==self.view.frame.origin.y)
        return;
    
    if(!self.navigationController.navigationBar.isHidden)
    {
        [self.navigationController.navigationBar setHidden:YES];
    }
    
    if(!self.tabBarController.tabBar.isHidden)
    {
        [self.tabBarController.tabBar setHidden:YES];
    }
    }
    

    【讨论】:

    • beginAnimations \ commitAnimations?这种动画视图的方式早已被弃用,取而代之的是基于块的动画。
    • 不调整内部/内容视图的大小
    • 只是我尝试了导航栏和标签栏,仅当您想调整内部/内容视图的大小时参考此代码如果有疑问请尝试自己,我会帮助您
    • 是的,这就是我想要做的(移动导航和标签栏+调整内部内容的大小),正如问题和赏金中所说的那样。您可以在问题中阅读我尝试了许多类似的事情,但都没有奏效
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 2012-08-24
    • 2011-08-08
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    相关资源
    最近更新 更多