如何“合法地”在应用程序中淡化导航栏?

16
似乎唯一可以通过向上滑动来移除带有动画的导航栏。我想要它像照片应用程序中那样淡出。
最简单的方法是改变alpha,但是苹果的指南指出:
在iOS v5.0之前,当与导航控制器一起使用时,您只能对导航栏进行少量直接自定义。具体而言,修改barStyle、tintColor和translucent属性是可以的,但是绝不能直接更改UIView级别的属性,例如frame、bounds、alpha或hidden属性。
语言有点奇怪,因为它声明了iOS 5之前的情况,但它声明了你不能直接更改alpha值,而且从未说明现在允许这样做。
我不想让我的应用被拒绝。
如何像状态栏一样淡出导航栏?

Hmmmm...如何才能“修改[…]半透明属性”却“永远不直接改变[…]alpha…”是可以的吗???如何在不改变alpha的情况下改变半透明度??? - verec
1
同意,我也很困惑。编辑:有一个“半透明”属性可以访问,但它只是YES或NO,并且设置为YES并不完全透明。 - Doug Smith
我会把“你绝不能…”理解为一种警告,意味着如果你尝试,某些东西可能会出错,而不是拒绝的威胁。应用商店的拒绝也可能发生,但更有可能是因为你的应用程序出现故障,而不是因为没有听从警告而受到惩罚。 - Caleb
你为什么把我的答案标记为正确并将赏金给了别人? - TomSwift
6个回答

13

解决这个问题很有趣。

本来应该非常简单:使用UIView transitionWithView在导航栏的隐藏和非隐藏状态之间执行交叉淡入淡出,如公共API setNavigationBarHidden:animated:所设定的那样。实际上,这对于"淡出"导航栏是有效的,但将其淡入时会出现问题。问题在于,无论是否在调用setNavigationBarHidden:animated:时指定了动画,导航栏都会滑动到位,而UIView +transitionWithView:不会动画化可动画属性(例如frame),除非你指定UIViewAnimationOptionAllowAnimatedContent

对我来说,这意味着内部的UINavigationController无论在调用setNavigationBarHidden:animated:时指定了动画,仍会在一个动画块中重新定位UINavigationBar。当animate:设置为NO时,此动画块的持续时间可能设置为'0'。

解决方案是在交叉淡入淡出转换之前以可见状态(不带动画)设置导航栏。这确保了导航栏从正确位置开始交叉淡入淡出,并且交叉淡入淡出仅会显示新的非隐藏状态。

我的示例项目是标准的单视图应用程序。在Storyboard中,有一个UINavigationController,它是入口点。我将此控制器的UINavigationBar的条形样式设置为黑色半透明(类似于照片应用)。导航控制器的rootViewController是一个简单的UIViewController,具有填充整个边界的UIImageView(也像照片应用一样)。我在视图上添加了一个 UITapGestureRecognizer来调用以下代码,在视图控制器中:

- (IBAction) onShowHideNavbar: (id) sender
{
    BOOL hide = !self.navigationController.navigationBarHidden;

    if ( !hide)
    {
        [self.navigationController setNavigationBarHidden: hide animated: NO];
    }

    [UIView transitionWithView: self.navigationController.view
                      duration: 1
                       options: UIViewAnimationOptionTransitionCrossDissolve
                    animations: ^{

                        [self.navigationController setNavigationBarHidden: hide animated: NO];
                    }
                    completion: nil ];
}

话虽如此,我认为您直接操作UINavigationBar的隐藏或透明度属性不会导致任何问题(Apple拒绝)。文档警告不要触摸它们,因为它们由UINavigationController管理,并更改它们可能会有看不见的后果。但在我看来,它们是公共API,因此使用它们不应该成为被拒绝的原因。


当您在UINavigationController上显示或隐藏导航栏时,内容视图框架会发生变化。因此,主控制器会被调整大小。如果您使用此代码,则会遇到两个问题:首先,在动画结束之前,您将看到导航栏后面的黑色背景(如果您使用带有黑色边框的图像,例如photos.app,则可能看不到它)。其次:动画完成后,主视图将自动重新调整大小,而没有动画效果,会出现跳跃。这并不好。甚至在淡入期间也存在问题,请参见此处:[链接](http://youtu.be/jdChWTkPGGw) - LombaX
是的,使用不透明的导航栏可以解决跳跃问题。如果将导航栏设置为半透明黑色(如我在答案中所说),它就不会跳跃,因为内容视图已经在其后面了。如果需要完全不透明的导航栏,则需要进行更多的工作。 - TomSwift
你说得对,是我的错误。当你使用半透明的导航栏时,视图会延伸到导航栏下方,因此动画不会出现“跳跃”。对于不透明的导航栏,像你在聊天中建议的那样,应该使用wantsFullScreenLayout属性,但由于某些原因我无法使其正常工作。 - LombaX

6
您无法合法地对UINavigationController的导航栏属性进行动画处理。
不过,您可以显示一个带有隐藏导航栏的导航控制器(始终隐藏或仅在特定视图控制器上隐藏),并用您“特殊”的 UINavigationBar 实例替换它;-)
我附上了一个示例项目(我使用了Xcode模板来快速创建它):FakeNavBar 请查看 DetailViewController viewDidLoad方法:
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self configureView];

    self.fakeBar = [[UINavigationBar alloc]initWithFrame:self.navigationController.navigationBar.bounds];


    UIBarButtonItem *back = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(back)];



    UINavigationItem *backItem = [[UINavigationItem alloc]initWithTitle:@"Back"];
    backItem.leftBarButtonItem = back;
    [self.fakeBar pushNavigationItem:backItem animated:YES];

    [self.view addSubview:self.fakeBar];

}

这是最终产品的视频(在YouTube上)

现在这真是太酷了。我回家后会试试看。谢谢你。 - Doug Smith
1
这个明显的问题是你的伪造的“返回”按钮没有被设计成一个返回按钮(没有左指向箭头)。如果你使用标准的UIViewController navigationItem属性(例如在nib/storyboard中设置它们),你还需要跳过一堆障碍。我不喜欢用这种方式重新发明轮子。 - TomSwift
1
@TomSwift,首先,如果这个踩的是你的答案,我希望你知道,根据你的声誉,踩应该是给错误答案而不是你不喜欢的答案。现在,谈论这个答案,返回按钮并不是一个问题:因为解释导航项堆栈如何工作并不是回答的目标,我更愿意只关注主要问题。为了避免这个“问题”,只需添加一个导航项堆栈即可将正常箭头作为返回按钮。此外,(继续下一条评论) - LombaX
如果您在这种情况下知道并且强烈理解事物的工作原理,那么没有必要“跳过一堆障碍”。例如,您可以将所有代码放在超类中,并复制原始导航控制器的导航项。请检查此最后一个示例(再次:5分钟代码,现在您可以在nib中设置标题或附加按钮):[链接](http://www.lombax.it/Documents/FakeNavBar2.zip)(仅在要启用特殊行为的VC上使用超类,即使具有更长的导航堆栈,这也将起作用,显然需要进行一些调试)。 - LombaX
听起来像是一堆跳圈圈的事情。;) - TomSwift
显示剩余2条评论

2
绕过这个问题的方法(我不知道你计划如何动画到下一个视图或以何种方式将淡入淡出融入其中)是将当前视图渲染为uiimage,使用该图像创建全屏幕UIImageView(基本上用其图片替换现有的UIView),在图像视图后面交换新视图,然后淡出图像视图。
您还可以裁剪出图像的UInavigationBar部分,并在过渡后淡化它。 这样,您就可以对“UINavgationBar”应用任何图像效果,而不会被苹果拒绝。

遗憾的是,在一个由苹果控制的世界中,像这样的技巧通常是获得某些效果的唯一方法。可能会有更好的方法,我只是给你提供一个想法,如果你找不到其他方法的话。 - Bergasms
UIView 中有一个新的“快照”支持(在 b4 中刚刚更改),可能会有所帮助? - verec
不幸的是,淡入淡出发生时,我需要在UIView中发生一些事情,因此静态图像也无法使用。并且,对于现在来说,它必须与iOS 6一起使用,这是不幸的。 - Doug Smith
他可以简单地使用自定义的导航栏,而不使用NavController提供的导航栏,这很简单...Doug,看看我的回答;-) - LombaX
干得好,LombaX。这就是我所说的更好的方式。 - Bergasms

1
我不知道有没有苹果认可的方法来做到这一点。我怀疑你修改navigationBar的alpha值不会被拒绝,但和你一样,我也不确定。当然,你可以实现自己的导航控制器/导航栏,你可以在其中做任何想做的事情。这就是我在我的应用程序中需要做类似于这样的事情时所做的。苹果对此非常满意。额外加分:如果使用自定义控件,苹果可以随意更改他们的控件而不会破坏你的布局。最近,在我们应用程序的某些区域中,这是一个问题,因为在不同版本的iOS(7)中,应用程序看起来完全不同。无论如何,这是我的两分钱……

0

如果您继续阅读,这是在引用后的下一段:

在iOS v5.0及更高版本中,您可以使用“自定义栏外观”中列出的方法来自定义栏的外观。您可以使用外观代理([UINavigationBar appearance])自定义所有导航栏的外观,也可以只自定义单个栏。

点击链接(Customizing the Bar Appearance)将显示以下内容:

自定义栏外观
tintColor 属性
– backgroundImageForBarMetrics:
– setBackgroundImage:forBarMetrics:
– titleVerticalPositionAdjustmentForBarMetrics:
– setTitleVerticalPositionAdjustment:forBarMetrics:
titleTextAttributes 属性

因此,在iOS v5.0及更高版本中,这是您可以(合法地)更改的列表。您可能可以通过更改tintColor的alpha或背景图像来使其看起来像是淡出(然后隐藏它),但我怀疑它不会看起来很对。尝试一下可能还是值得的。


0

你可能不能轻松地淡化导航栏,但是你可以轻松地淡化一个导航栏的图片。所以你可以做一件事情,就是创建一个导航栏的图像,用你的图像替换导航栏,然后通过透明度对图像视图进行动画处理,使其逐渐消失。或者你也可以反过来:在导航栏上方放置一个图像视图,并淡入一个代表导航栏后面内容的图像。


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