如何在iOS中平滑过渡从半透明到不透明的UINavigationBar?

31

在 iOS 7 和 8 中,我在视图之间进行转换时,重新配置 UINavigationBar 出现了问题。

我的应用程序当前包含以下 UIViewController 流程:

VC1 --> VC2 --> VC3

在这个流程中:

  • VC1 是主屏幕,并且具有不透明的 UINavigationBar
  • VC2 具有半透明的 UINavigationBar
  • VC3 回到具有不透明的 UINavigationBar

我遇到的问题是这些视图之间的过渡看起来都非常粗糙。首先我尝试了以下方法:

在 VC2 中:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    // configure appearance
    [self.navigationController.navigationBar configureTranslucentAppearance];
}

并且在VC1和VC3中

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    // configure appearance
    [self.navigationController.navigationBar restoreDefaultAppearance];
}

这里是上述两个辅助函数的实现:

- (void)restoreDefaultAppearance {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

    [self setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor JTTextNavBar]}];
    [self setTintColor:[UIColor JTTextNavBar]];
    [self setBarTintColor:[UIColor JTBackgroundNavBarWithAlpha:1.0]];
    [self setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
    [self setBackgroundColor:[UIColor JTBackgroundNavBarWithAlpha:1.0]];
    [self setShadowImage:[UIImage navigationBarShadowImage]];
    [self setTranslucent:NO];
}

- (void)configureTranslucentAppearance {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

    [self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
    [self setBackgroundColor:[UIColor clearColor]];
    [self setShadowImage:[UIImage new]];
    [self setTranslucent:YES];
}

这是处理此转换的最基本方法,但会产生以下视觉伪影:

  • 从VC1 --> VC2 的转换开始时,导航栏立即变为黑色。然后动画正常完成 enter image description here
  • 从VC2 --> VC1 的转换中,在segue足够完成之前,导航栏立即更改为应用程序的默认颜色。 enter image description here
  • 从VC2 --> VC3 的转换中,导航栏立即从半透明变成应用程序导航栏颜色,然后菜单项和 VC body 开始动画。 enter image description here
  • 从VC3 --> VC2 的转换中,导航栏立即变为黑色,并保持此状态直到segue完成。 enter image description here

所有这些转换看起来都不好。理想情况下,我希望视图能够与它们的新UINavigationBar平稳过渡,但我所见过成功实现这一点的唯一方法是在每个XIB中手动添加工具栏。

有什么建议吗?如果这个描述让人困惑,我很抱歉 :(

编辑:已添加所列转换中UINavigationBarUIViewController顶部的裁剪图像。


你尝试过使用 +[UIView animateWithDuration:animations:] 来实现这些变化的动画效果吗? - Palpatim
是的。它改善了转换,但仍然从不透明到透明,因此在segue结束时,您可以看到导航栏后面的黑色。到目前为止,我发现的最佳解决方法是在执行[self.navigationController.navigationBar configureTranslucentAppearance];之前将UINavigationBar backgroundColor设置为应用程序默认值。这至少不会在从不透明到半透明时显示临时黑色栏,尽管仍存在从半透明到不透明的问题。感谢您的建议。 - alexgophermix
不确定这对您是否仍然是一个问题,但似乎导致不良影响的关键因素是translucent属性的更改。与过渡的其余部分不同,透明度似乎会立即应用。建议在viewDidAppear中单独设置translucent属性,这样可以延迟推送/弹出开始时看到的突然变化。特别是当viewWillAppear将此从NO更改为YES时,将该属性更改延迟到viewDidAppear。虽然完全透明度要延迟到设置为YES,但应该可以消除黑色条纹效果。 - Rory McKinnel
这大致是我在发布这里之前所做的。我希望有一些模式或解决方法是我错过了的,但是在VC1和VC2中修改背景外观和半透明度的组合似乎是唯一的解决方案,而不完全覆盖导航栏或其转换,如下面的答案建议的那样。 - alexgophermix
最佳解决方案在这里:https://dev59.com/HnRA5IYBdhLWcg3wvQlh#2406167 - Mohammad Zaid Pathan
11个回答

20

我终于找到了一个不错的解决方案!

目前看来,似乎没有一种适当的方式可以从不透明的 UINavigationBar 平滑过渡到透明,但是您可以从具有可见状态栏的视图控制器平滑地过渡到隐藏状态栏的视图控制器。

这为添加以下内容提供了可能的解决方法,即在上面的 VC2viewWillAppear 中添加:

[self.navigationController setNavigationBarHidden:YES animated:YES];

一旦您完成这些,手动添加一个 UINavigationBar 到您的 xib 文件,并将其配置为透明(同时添加所有必要的 UIBarButtonItem 和视图)。

如果一切都正确连接,从VC1 切换到VC2 时,会以与视图转换相同的速度隐藏 UINavigationBar,并且 VC2 将显示其嵌入的 UINavigationBar

注意:要使此功能正常工作,您需要确保在可以从 VC2 访问的视图控制器的 viewWillAppear 中通过以下方式重置 UINavigationBar 的可见性(如果有必要):

[self.navigationController setNavigationBarHidden:NO animated:YES];

简短概述 - 在透明的导航栏视图控制器中手动添加UINavigationBar,并在其viewWillAppear方法中通过setNavigationBarHidden:animated:来隐藏默认的导航栏。


2
请记住,当取消后滑手势时,setNavigationBarHidden:animated:存在缺陷。https://dev59.com/HWIj5IYBdhLWcg3wwHtr - qnoid

18

你看到的黑色是 UINavigationController 视图的背景颜色。一个减少这种情况发生的方法是将该视图的背景颜色调整为即将出现或正在出现的视图控制器的视图颜色。如果使用的是纯色,这个方法效果很好。另一种方法是通过 UIViewController.extendedLayoutIncludesOpaqueBars = YES; 来扩展你的视图到不透明导航栏的后面。


7
navigationController?.view.backgroundColor设置为我所需的不透明的导航栏颜色对我来说非常关键。这样可以防止出现不想要的黑色。非常感谢! - marcshilling

4

将UIWindow的背景颜色设置为导航栏的tintColor。


3
始终使用半透明效果,再添加一个带有颜色的UIView并在其下方进行动画处理,以增加视觉效果。

1

我也曾经遇到了这个问题。

不幸的是,我没有成功地通过Storyboard添加自己的导航栏。相反,我在ViewController中添加了一个视图,它具有不透明的导航栏和负边距,并延伸到导航栏下方。

当推送具有透明导航栏的ViewController时,该栏立即变为透明,但由于我直接放置在其后面的颜色相同的视图,所以变化并不明显。Et voila。

我编写的代码非常基础,使用C#(Xamarin)编写,但出于完整性的原因....

  var backing = new UIView(new CGRect(0, -68, this.View.Frame.Width, 64f));
  backing.BackgroundColor = UIColor.Green;
  this.View.AddSubview(backing);

1
我曾经遇到过一个几乎相同的问题。使用导航栏或工具栏作为模糊效果时,并没有任何平滑的过渡方式。我找到的最佳方案*是将视图制作成图像,然后将该图像用于过渡。特别是如果您只需要它用于过渡,这是最便宜但仍提供出色UI / UX的选项。
*唯一的一个注意点是,在将UIView作为图像进行快照,截屏或光栅化时,导航栏和工具栏中的某些UI效果不会显示出来。如果仅用于过渡,则可以忽略此问题。

1
你可以使用UIView来创建自己的导航栏,这样你就可以完全控制它的外观、布局、淡入淡出等。我有一些时间以前放弃了使用UINavigationBar,因为它很难处理,正如你所发现的那样,现在我从来不再使用它。
我有一个根视图控制器,其中包含一个表示导航栏的UIView。如果我想要添加返回按钮、更改颜色、显示或隐藏导航栏、更改透明度等操作,则所有这些都由RVC控制,其他视图控制器调用RVC的方法来根据它们的外观需求更改导航栏。
RVC具有一个容器视图,其大小为控制器视图的全尺寸,其他视图控制器加载到其中。需要一些配置才能将所有内容设置好,但一旦完成,这是一种可以快速在每个使用导航栏的项目中使用的结构。

你把这个做成了CocoaPod吗? - sbru

0

我还有一个解决方案..针对那些正在重构项目且无法在每个地方添加导航栏的人..这不是完美的解决方案,就像@alexgophermix的一个一样。但也不错:

let transition = CATransition()
        transition.duration = 0.6
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        transition.type = kCATransitionFade
        navigationController?.navigationBar.shadowImage = UIImage()
        navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
        navigationController?.navigationBar.isTranslucent = true
        navigationController?.navigationBar.layer.add(transition, forKey: nil)
        navigationController?.view.backgroundColor = .clear

我在viewDidLoad()中设置了我的导航栏,这样可以使其在任何地方都起作用,并且具有略微不同的过渡效果。在我的情况下,我在某处设置了透明背景并添加了一个渐变图像。所以这个小改变对于任何地方都很适合。这种过渡效果并不太引人注目,但也不会显得糟糕:)


0

更改背景颜色对我有效

window?.backgroundColor = Color.red.toUIColor()

0
在我的情况下,我看到的黑色是导航栏的背景图像。我没有将其设置为nil,而是在viewWillAppear中将其设置为与透明导航栏后面视图的背景颜色相匹配。
let image = UIImage(color: .white)
navigationController?.navigationBar.setBackgroundImage(image, for: .default)

然后,在 willMoveviewWillDisappear 中,我将其恢复为原始颜色。


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