当UIViewController被推出时显示UITabBar

10
这是我的情况:
我有一个UITabBarController里面嵌套了一个UINavigationController。当我在导航控制器中向下浏览时,某个时刻我需要隐藏UITabBar,因为我希望视图具有尽可能多的空间。
我通过在推入的UIViewController中使用self.hidesBottomBarWhenPushed = YES来实现这一点,效果非常好。
然而,我想在以下推送的控制器中再次显示UITabBar。我尝试在其他控制器中放置self.hidesBottomBarWhenPushed = NO,但是UITabBar不会重新出现。

根据文档说明,这似乎很正常:

hidesBottomBarWhenPushed

如果为YES,则屏幕底部的栏将被隐藏;否则为NO。如果为YES,则底部栏将保持隐藏状态,直到从堆栈中弹出视图控制器。

确实,当设置此属性为yes的控制器被弹出时,选项卡栏将会出现。

有没有适当的方法来显示已经被隐藏的UITabBar当控制器被推送时?

提前感谢。


3个回答

13

hidesBottomBarWhenPushed并没有被弃用。 我发现使用以下方法实现隐藏和显示UITabBar非常简单:

self.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
self.hidesBottomBarWhenPushed = NO;

所以,在您推送detailViewConroller之后,应将hide属性重置为NO。这样,当详细视图弹出时,它将再次显示出来。在viewWillApear / disapear等方面不需要进行任何其他更改...享受吧。


1
我希望我能多次点赞。这个解决方案让我省去了一整天的头疼,因为我一直在尝试让一些视图与选项卡栏正确地配合工作。所以感谢您提供的帮助。 - Bill Burgess
你的解决方案对我来说似乎有效,但仍存在问题。我有许多屏幕可以导航。因此,在每个地方编写相同的代码是不可行的。您能否请解释一下为什么这个hack起作用?谢谢! - GoGreen
1
我建议您创建一个UINavigationController的子类,并将其设置为所有导航控制器的类型。在其实现中,重写以下方法: viewController.hidesBottomBarWhenPushed = YES; [super pushViewController:viewController animated:animated]; }现在,每当您推送一个新的视图控制器时,它将隐藏标签栏。 - AmitP

7

好的,我们还有很长的路要走。

从文档中可以看出,默认行为是:一旦视图控制器的hides...属性为YES,则标签栏在视图控制器弹出之前都会被隐藏。你想要的直接与此相矛盾,出于各种原因,我首先建议不要采取这种方法。

然而,这并不意味着无法实现。


  • hides...属性设置回NO

您无法修改默认行为。要显示标签栏,堆栈中所有视图控制器都必须将其hides...属性设置为NO。因此,如果您想在推入新视图时再次显示该栏,您必须将上一个视图控制器的hides...属性再次设置为NO

在推入新视图控制器之前,将该属性设置回NO。

// ... prepare viewControllerToPush

self.hidesBottomBarWhenPushed = NO;
[self.navigationController pushViewController:viewControllerToPush animated:YES];
[viewControllerToPush release];

通过这样做,您将再次拥有选项卡栏。但是,您会发现选项卡栏从左侧推入,而新视图从右侧推入。显然,这是不可取的,因此我们需要修复它。
覆盖选项卡栏的层操作
问题在于,当选项卡栏再次出现时使用的默认图层动画是从左侧进行的推送转换动画。UITabBar实现了 - (id< CAAction >)actionForLayer:(CALayer *)layer forKey:(NSString *)key方法,告诉它在这种情况下使用从左侧进行的动画。我们需要覆盖此方法,以返回一个从右侧进行的动画。
为了显示选项卡栏,Cocoa修改其图层的position属性。因此,我们的新方法应该为键position返回一个从右侧进行的动画,并且对于所有其他键,它应该使用默认实现。请注意,使用position来控制选项卡栏没有被苹果官方文档记录,因此在后续版本中可能会更改,而我们正在直接违反苹果的规范,所以不能抱怨太多。
但是,您不能仅使用子类化来覆盖该方法。因为即使您有UITabBar的自定义子类,也无法修改UITabBarController类以使用它来代替默认的UITabBar。
因此,它变得更加复杂。为了将自己的逻辑植入UITabBar类中,您必须“交换”消息actionForLayer: forKey:的实现。
首先,使用类别向UITabBar类添加一个新方法。
@interface UITabBar(customAction)
@end

@implementation UITabBar(customAction)

- (id < CAAction >)customActionForLayer:(CALayer *)layer forKey:(NSString *)key {
    if ([key isEqualToString:@"position"]) {
        CATransition *pushFromRight = [[CATransition alloc] init];
        pushFromRight.duration = 0.25; 
        pushFromRight.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; 
        pushFromRight.type = kCATransitionPush; 
        pushFromRight.subtype = kCATransitionFromRight;
        return [pushFromRight autorelease];
    } 
return [self defaultActionForLayer:layer forKey:key];
}
@end

在标签栏控制器的viewDidAppear方法中,插入以下代码。
Method original = class_getInstanceMethod([UITabBar class], @selector(actionForLayer:forKey:));
Method custom = class_getInstanceMethod([UITabBar class], @selector(customActionForLayer:forKey:));

class_replaceMethod([UITabBar class], @selector(actionForLayer:forKey:), method_getImplementation(custom), method_getTypeEncoding(custom));
class_addMethod([UITabBar class], @selector(defaultActionForLayer:forKey:), method_getImplementation(original), method_getTypeEncoding(original));

您需要在 viewDidAppear 中执行此操作,而不是在 viewDidLoad 中执行,因为否则选项卡栏将在应用程序首次显示时从右侧滑入。

现在,当 UITabBar 实例收到消息 actionsForLayer forKey: 时,会调用自定义方法 customActionForLayer forKey:。它拦截键 position,并从右侧返回动画。如果是其他键,则调用消息的原始实现,该实现现在连接到消息 defaultActionsForLayer forKey:


好的,我们完成了。请记住,在弹出视图时,您可能需要将 hides... 属性重新设置为 YES,因为在推送新视图时将其设置为 NO(并进行一些类似的技巧以正确地进行动画)。

我花了一些时间来做这个,但具有讽刺意味的是,我必须再次说“不要再使用这个”,因为它使用了 Cocoa 类的未记录信息(用于选项卡栏动画的“position”键),与记录下来的行为相矛盾,并违反了 Apple 的人机界面指南。您可能会发现,您的应用程序无法在以下 SDK 版本中正常工作,用户无法轻松适应界面,甚至 Apple 可能会拒绝在 App Store 上发布您的应用程序。

那么我的答案到底是什么呢?嗯,我猜它是 iOS 开发中一些有趣主题的示例(以及证明我今天非常没效率 :P)。


哇!那是一個非常詳盡的回答。謝謝! 至少你在拖延時還是很有幫助的^^。但由於它使用了一些未記錄的東西,我會遵循你的建議:我不會使用那個:p。所以要麼用其他方式隱藏選項卡欄,而不使用hidesBottomBarWhenPushed,或者只是在工具欄的其他地方顯示按鈕... - kombucha

6

如果在iOS 5中使用Storyboards,我的方法如下:

在执行push segue之前将hidesBottomBarWhenPushed属性设置为NO

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
   if( [segue.identifier isEqualToString:@"PushDetailView"] )
   {
      self.hidesBottomBarWhenPushed = NO;
   }

   [super prepareForSegue:segue sender:sender];
}

转场标识符的命名显然由您自己确定。当视图控制器的视图即将消失时,立即将其设置回YES
- (void)viewWillDisappear:(BOOL)animated
{
   self.hidesBottomBarWhenPushed = YES;

   [super viewWillDisappear:animated];
}

测试过(在iOS 5.1上)非常流畅,使用了正确的动画效果来呈现UITabBar


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接