hidesBottomBarWhenPushed = NO不工作?

时间:2011-04-12 20:49:20

标签: iphone objective-c uitabbarcontroller uitabbar

我的应用中有一个UITabBar,我将其隐藏在第一个标签中的第一个UIViewController,将此行放在A ppDelegate 中:

// ... in MyAppDelegate.m
firstViewController.hidesBottomBarWhenPushed = YES;

firstViewController中,用户可以推送在同一标签中推送新UIButton的{​​{1}}。当发生这种情况时,我希望UIViewController再次可见。我试图让它像这样回来:

UITabBar

不幸的是,不会带回//... in firstViewController.m secondViewController = [[SecondViewController alloc] init]; secondViewController.hidesBottomBarWhenPushed = NO; [[self navigationController] pushViewController:secondViewController animated:YES]; 。它仍然隐藏着。

如何在隐藏后UITabBar正确绑定栏?

提前致谢。

9 个答案:

答案 0 :(得分:67)

这个问题一直困扰着我,我只是找到了一个有效的解决方案。 hidesBottomBarWhenPushed属性是一种非常奇怪的野兽,在我看来,这是一种反直觉的方式。

问题在于,当你按下一个新的视图控制器(或弹回)时,如果想要隐藏底栏,navigationController会询问所有视图控制器(从上到下),如果任何他们说YES标签栏将被隐藏,这就是为什么标签栏保持隐藏,尽管设置NO隐藏在新的视图控制器上。

这是我的解决方案 - 覆盖视图控制器中您希望没有标签栏的hidesBottomBarWhenPushed getter,并检查它是否位于堆栈顶部:

目标-C

- (BOOL) hidesBottomBarWhenPushed
{
    return (self.navigationController.topViewController == self);
}

斯威夫特(不那么明显,因此片段)

override var hidesBottomBarWhenPushed: Bool {
    get {
        return navigationController?.topViewController == self
    }
    set {
        super.hidesBottomBarWhenPushed = newValue
    }
}

这很好地将hide / show逻辑封装在一个地方,所以你不必在隐藏的viewcontroller之外考虑它。

答案 1 :(得分:28)

这是hidesBottomBarWhenPushed的文档(强调添加):

  

如果是,则底栏保持隐藏,直到视图控制器从堆栈中弹出。

所以看起来你所看到的行为就是文档所说的内容。首先将视图控制器推送到具有hidesBottomBarWhenPushed = YES的堆栈。此时,将其他视图控制器推入堆栈将不会更改底栏的隐藏性。只要第一个视图控制器在堆栈上,底栏就会保持隐藏状态。

所以我认为你必须想出一种不同的方法来实现你的UI目标。一种选择是在标签栏控制器的视图上将第一个视图控制器显示为模态视图控制器。然后,当你想要转到第二个视图控制器时,只需关闭第一个视图控制器即可。唯一的视觉差异将是过渡动画。

肯定还有其他选择,但这首先出现在我的脑海中。

祝你好运!

答案 2 :(得分:9)

我有同样的问题,但3小时后我找到了解决方案! 在这个主题answered Oct 8 '10中,Dave Batton说:

  

使用 hidesBottomBarWhenPushed 属性的正确方法是:

self.anotherViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:self.anotherViewController animated:animated];

答案 3 :(得分:2)

不确定是否找到了解决方案,但我只是设法使其正常工作。

我的情景:

我有UITabBarController个标签栏项目。 在其中一个标签栏项目上,它会加载一个带有按钮的UIViewController。按钮调用IBOutlet函数,该函数加载另一个UIViewController,其中包含底部的标签栏。

经过多次试验和试验错误........

IBOutlet功能上,我执行以下操作:

{
 self.hidesBottomBarWhenPushed = YES;
 /* Push the new controller with tab bar */
}

这种方式正常,UITabBarController's标签栏向左滑动,推出控制器的标签栏从右侧滑动。

显然,从功能角度来看,我需要在“返回”时将初始UITabBarController's tar栏重新推入。

经过多次试验和试验错误........

我在viewWillDisappear中使用UIViewController方法将标签栏推送到UIViewController

- (void) viewWillDisappear:(BOOL)animated
{
    self.hidesBottomBarWhenPushed = NO;
}

我在模拟器中对此进行了一些快速测试,看起来效果很好。

一些贡献者认为这是糟糕的用户界面,但我现在正在尝试这样做,看看它是如何工作的。

很高兴收到(警察)任何反馈意见。 :)

答案 4 :(得分:1)

我认为你误解了hidesBottomBarWhenPushed使用。如果是,则底部栏保持隐藏状态,直到视图控制器从堆栈中弹出。

所以,如果我理解你的问题:

secondViewController应该是YES,firstViewController应该是NO。

答案 5 :(得分:1)

使用

secondViewController.hidesBottomBarWhenPushed = NO;

插入一些操作时

firstViewController.hidesBottomBarWhenPushed = YES;

答案 6 :(得分:1)

通过继承UINavigationController,我想出了一个非常容易解决这个问题的方法。您可以通过使用具有关联对象的类别来完成同样的事情,但我已经有了一个子类,所以我只是将代码放在那里。

首先,在你的UINavigationController中添加一个ivar:

@interface CustomNavigationController ()
{
    NSMutableSet *_viewControllersWithHiddenBottomBar;
}

@end

然后我覆盖push和pop方法来接管处理隐藏逻辑:

- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if(viewController.hidesBottomBarWhenPushed)
    {
        viewController.hidesBottomBarWhenPushed = NO;
        [_viewControllersWithHiddenBottomBar addObject:viewController];
        [self rootViewController].hidesBottomBarWhenPushed = YES;
    }
    else
    {
        [self rootViewController].hidesBottomBarWhenPushed = NO;
    }
    [super pushViewController:viewController animated:animated];
}

- (UIViewController *) popViewControllerAnimated:(BOOL)animated
{
    if([_viewControllersWithHiddenBottomBar containsObject:self.viewControllers[self.viewControllers.count - 2]])
    {
        [self rootViewController].hidesBottomBarWhenPushed = YES;
    }
    else
    {
        [self rootViewController].hidesBottomBarWhenPushed = NO;
    }
    UIViewController *poppedViewController = [super popViewControllerAnimated:animated];
    [_viewControllersWithHiddenBottomBar removeObject:poppedViewController];
    return poppedViewController;
}

- (UIViewController *) rootViewController
{
    return ((UIViewController *)self.viewControllers.firstObject);
}

我使用hidesButtomBarWhenPushed属性来填充集合,然后在viewcontroller级别重置值(因为如果任何视图控制器都设置了此属性,那么它上面的所有内容也会隐藏它)。为简单起见,我使用根视图控制器来控制根据集合中的值显示和隐藏标签栏。

您还需要在某处初始化该集,我刚使用initWithRootViewController:

这对我来说非常无缝,并且是我能想到的最不好的方式,不需要接管任何现有的动画并且必须处理边缘情况。

答案 7 :(得分:0)

这个对我有用。 感谢这里的一些其他线程的提示,我找到了一个解决方案来隐藏一个视图cotroller的标签栏,并为从内部调用的任何视图控制器重新建立它。

这样做,我可以跟上常规的导航控制器链。

这就是我最终得到的:

#define kTabBarHeight               49 // This may be different on retina screens. Frankly, I have not yet tried.

- (void) hideTabBar:(BOOL)hide {

    // fetch the app delegate
    AppDelegate         *delegate   = [[UIApplication sharedApplication] delegate];

    // get the device coordinates
    CGRect              bounds      = [UIScreen mainScreen].bounds;
    float               width;
    float               height;

    // Apparently the tab bar controller's view works with device coordinates  
    // and not with normal view/sub view coordinates
    // Therefore the following statement works for all orientations. 
    width                   = bounds.size.width;
    height                  = bounds.size.height;

    if (hide) {

        // The tab bar should be hidden too. 
        // Otherwise it may flickr up a moment upon rotation or 
        // upon return from detail view controllers. 
        [self.tabBarController.tabBar setHidden:YES];

        // Hiding alone is not sufficient. Hiding alone would leave us with an unusable black
        // bar on the bottom of the size of the tab bar. 
        // We need to enlarge the tab bar controller's view by the height of the tab bar. 
        // Doing so the tab bar, although hidden, appears just beneath the screen. 
        // As the tab bar controller's view works in device coordinations, we need to enlarge 
        // it by the tab bar height in the appropriate direction (height in portrait and width in landscape)
        // and in reverse/upside down orientation we need to shift the area's origin beyond zero. 
        switch (delegate.tabBarController.interfaceOrientation) {
            case UIInterfaceOrientationPortrait:
                // Easy going. Just add the space on the bottom.
                [self.tabBarController.view setFrame:CGRectMake(0,0,width,height+kTabBarHeight)];
                break;

            case UIInterfaceOrientationPortraitUpsideDown:
                // The bottom is now up! Add the appropriate space and shift the rect's origin to y = -49
                [self.tabBarController.view setFrame:CGRectMake(0,-kTabBarHeight,width,height+kTabBarHeight)];
                break;

            case UIInterfaceOrientationLandscapeLeft:
                // Same as Portrait but add the space to the with but the height
                [self.tabBarController.view setFrame:CGRectMake(0,0,width+kTabBarHeight,height)];
                break;

            case UIInterfaceOrientationLandscapeRight:
                // Similar to Upside Down: Add the space and shift the rect. Just use x and with this time
                [self.tabBarController.view setFrame:CGRectMake(0-kTabBarHeight,0,width+kTabBarHeight,height)];
                break;

            default:
                break;
        }
    } else {
        // reset everything to its original state. 
        [self.tabBarController.view setFrame:CGRectMake(0,0,width,height)];
        [self.tabBarController.tabBar setHidden:NO];
    }

    return; 
}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{

    // It is important to call this method at all and to call it here and not in willRotateToInterfaceOrientation
    // Otherwise the tab bar will re-appear. 
    [self hideTabBar:YES];

    // You may want to re-arrange any other views according to the new orientation
    // You could, of course, utilize willRotateToInterfaceOrientation instead for your subViews. 
}

- (void)viewWillAppear: (BOOL)animated { 

    // In my app I want to hide the status bar and navigation bar too. 
    // You may not want to do that. If so then skip the next two lines. 
    self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];

    [self hideTabBar: YES];

    // You may want to re-arrange your subviews here. 
    // Orientation may have changed while detail view controllers were visible. 
    // This method is called upon return from pushed and pulled view controllers.   

    return;
}

- (void)viewWillDisappear: (BOOL)animated {     

    // This method is called while this view controller is pulled
    // or when a sub view controller is pushed and becomes visible
    // Therefore the original settings for the tab bar, navigation bar and status bar need to be re-instated

    [self hideTabBar:NO];

    // If you did not change the appearance of the navigation and status bar in viewWillAppear,
    // then you can skip the next two statements too. 
    self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];

    return;
}

内联评论应该解释每个陈述的推理。虽然,可能有更聪明的编码方式。

与隐藏状态栏和导航栏一起有一个副作用,我不想向你们隐瞒。 1.从此导航控制器返回到主叫导航控制器时,调用控制器上的状态栏和导航栏会重叠,直到设备旋转一次或直到另一个选项卡出现后再次选择相关选项卡。 2.当调用视图控制器是一个表视图,并且当设备在返回到表格时处于横向模式时,表格将以适当的横向方向显示,但它的布局就像是纵向一样。左上角很好,但屏幕下方隐藏了一些表格单元格和标签栏。在右手边有一些自由空间。这也是通过再次旋转设备来解决的。

一旦我找到了解决这些轻微但令人讨厌的错误的解决方案,我会及时通知您。

答案 8 :(得分:0)

将View控制器设置为

我不需要选项卡中的设置屏幕,这是过去两种方法中的设置屏幕,也没有设置屏幕可推动显示底部选项卡中的任何屏幕。

override func viewWillAppear(_ animated: Bool) {

     self.hidesBottomBarWhenPushed = true
}

override func viewDidAppear(_ animated: Bool) {

    self.hidesBottomBarWhenPushed = false
}

谢谢,如果您有任何疑问可以询问。