当 UIViewController 推送时显示 UITabBar

发布于 2024-10-18 06:23:22 字数 631 浏览 7 评论 0原文

这是我的情况:
我在 UITabBarController 内有一个 UINavigationController 。当我向下钻取导航控制器时,在某些时候我必须隐藏 UITabBar 因为我希望视图拥有尽可能多的空间。
我通过在推送的 UIViewController 中使用 self.hidesBottomBarWhenPushed = YES 来实现这一点,并且效果很好。
但是,我想在以下推送的控制器中显示 UITabBar 。我尝试将 self.hidesBottomBarWhenPushed = NO 放入其他控制器中,但 UITabBar 不会回来。

正如文档所述,这似乎是正常的:

hidesBottomBarWhenPushed

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

事实上,当弹出此属性设置为 yes 的控制器时,选项卡栏确实会回来。

一旦隐藏了控制器,是否有任何正确的方法可以在按下控制器时显示UITabBar

提前致谢

Here's my situation :
I have a UINavigationController inside a UITabBarController. When I drill down the navigation controller, at some point I have to hide the UITabBar because I want the view to have as much space as possible.
I do that by using self.hidesBottomBarWhenPushed = YES inside the pushed UIViewController, and it works quite well.
However, I want to show the UITabBar back in the following pushed controllers. I've tried to put self.hidesBottomBarWhenPushed = NO in the other controllers, but the UITabBar won't come back.

It seems to be normal as the documentation states :

hidesBottomBarWhenPushed

If YES, the bar at the bottom of the screen is hidden; otherwise, NO. If YES, the bottom bar remains hidden until the view controller is popped from the stack.

And indeed, when the controller with this property set to yes is popped, the tabbar does come back.

Is there any proper way to show the UITabBar when a controller is pushed, once it's been hidden?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情未る 2024-10-25 06:23:23

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

当您从文档中阅读时,默认行为很清楚:一旦视图控制器的 hides... 属性为 YES,标签栏就会隐藏,直到视图控制器弹出。你想要的直接与此相矛盾,出于各种原因,我首先建议不要采取这种方法。

然而,这并不意味着它不可能实施。


  • hides... 属性设置回 NO

您无法修改默认行为。要显示选项卡栏,堆栈中的所有视图控制器必须将其hides...属性设置为NO。因此,从隐藏选项卡栏的视图来看,如果您想在推送新视图时再次显示选项卡栏,则必须将之前的视图控制器的 hides... 属性设置回不。

在推送新的视图控制器之前,将该属性设置回 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 属性。因此,我们的新方法应该从右侧为键位置返回一个动画,对于所有其他键,它应该使用默认实现。请注意,Apple 没有记录标签栏使用 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:。它拦截关键位置,并从右侧返回一个动画。如果它用于另一个键,它将调用消息的原始实现,该消息现在连接到消息 defaultActionsForLayer forKey:


好的,我们到了。请记住,当弹出视图时,您可能必须将 hides... 属性设置回 YES,因为在推送新视图时将其设置为 NO(并执行一些类似的技巧来正确设置动画) )。

我已经花了一些时间在这上面,但讽刺的是,我不得不说*不要再使用这个,因为它使用了 Cocoa 类的未记录信息(选项卡栏动画的“位置”键),矛盾已记录的行为,并且违反了 Apple 的人机界面指南。您可能会发现您的应用程序与以下 SDK 版本的工作方式不同,用户无法轻松采用该界面,甚至 Apple 在 App store 上拒绝您的应用程序。

那我的回答到底是为了什么?好吧,我想这是有关 iOS 开发的一些有趣主题的示例(并且证明我今天的效率非常低下:P)。

Okay, here have we an awfully long way to go.

As you read from the document, the default behavior is clear: once a view controller's hides... property is YES, the tab bar is hidden until the view controller is popped. What you want directly contradicts this, and for various reasons, I would first recommend not to take this approach.

However, it doesn't mean it is impossible to implement.


  • Setting hides... property back to NO

You cannot modify the default behavior. To show the tab bar, all view controllers in the stack must set their hides... property to NO. So from the view where the tab bar is hidden, if you want to show the bar again when a new view is pushed, you have to set the previous view controller's hides... property back to NO again.

Just before you push a new view controller, set the property back to NO.

// ... prepare viewControllerToPush

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

By doing this, you will have the tab bar again. However, you will recognize the tab bar is pushed in from the left, while the new view is pushed from the right. This is clearly not desirable, so we need to fix this.


  • Overriding the layer action for the tab bar

The thing is, the default layer action (the animation) used when the tab bar appears again, is a push transition animation from the left. UITabBar implements - (id < CAAction >)actionForLayer:(CALayer *)layer forKey:(NSString *)key method that tells to use the animation from the left for the case. We need to override this method, to return an animation from the right instead.

To show the tab bar back, Cocoa modifies its layer's position property. Therefore, our new method should return an animation from the right for the key position, and for all the other keys, it should use the default implementation. Note that using position for the tab bar is not documented by Apple, so it's subject to change without a notice in the following versions. We are implementing something directly contradicts Apple's specification anyway, so can't complain much.

However, you cannot just use subclassing to override the method. Because even if you have a custom subclass of UITabBar, you cannot modify UITabBarController class to use it instead of the default UITabBar.

So, it gets a bit more complex. In order to implant your own logic to UITabBar class, you have to 'swap' the implementation for the message actionForLayer: forKey:.

First, add a new method to UITabBar class using category.

@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

And in the viewDidAppear method of the tab bar controller, insert the following codes.

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

You want to do this in viewDidAppear rather than viewDidLoad, because otherwise the tab bar will slide in from the right in the first time the application shows up.

Now when a UITabBar instance gets a message actionsForLayer forKey:, the custom method customActionForLayer forKey: is invoked. It intercepts the key position, and returns an animation from the right. If it's for another key, it invokes the original implementation of the message, which is now connected to the message defaultActionsForLayer forKey:.


Okay, there we are. Remember when popping back the views, you may have to set the hides... property back to YES, because you set it to NO when pushing a new view (and do some similar tricks to animate it properly).

I've spent some time on this but ironically, I have to say *Do not use this again, because it uses an undocumented information of Cocoa classes ("position" key for tab bar animation), contradicts the documented behaviors, and is against Apple's human interface guideline. You may find out that your application wouldn't work the same with the following SDK versions, that the users cannot easily adopt the interface, or even that Apple rejects your application on App store.

Then what on earth is my answer for? Well, an example of some interesting topics on the iOS development, I guess (and a proof showing that I'm terribly unproductive today :P).

街道布景 2024-10-25 06:23:23

以下是我在 iOS 5 中使用 Storyboard 时的方法:

在执行推送转场之前将 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];
}

使用 UITabBar 的所有正确动画,像魅力一样工作(在 iOS 5.1 上测试)。

Here's my approach to this when using Storyboards in iOS 5:

Set the hidesBottomBarWhenPushed property to NO before performing the push segue:

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

   [super prepareForSegue:segue sender:sender];
}

The segue identifier is obviously up to you to name.

Set it back to YES immediately when the view controller's view will disappear:

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

   [super viewWillDisappear:animated];
}

Works like a charm (tested on iOS 5.1) using all the right animations for the UITabBar.

何以心动 2024-10-25 06:23:22

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

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

因此,在推入DetailViewConroller 后,您应该将隐藏属性重置回NO。
这样,当详细信息视图弹出时,它将再次显示。
不需要对 viewWillApear/disapear 等进行任何额外的更改。
享受。

hidesBottomBarWhenPushed is not deprecated.
I found it is very simple to achieve the hide and show UITabBar using the following method:

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

So right after you push the detailViewConroller, you should reset the hide property back to NO.
This way it will show up again when the detail view pops back.
no need for any additional changes in viewWillApear/ disapear,etc..
Enjoy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文