172

iOS 7.1 更新:在此更新中,修改 UINavigationBar 中的 alpha 通道的解决方法似乎已被忽略。现在,最好的解决方案似乎是“处理它”,并希望你选择的任何颜色都能呈现半透明的效果。我仍在寻找解决此问题的方法。


iOS 7.0.3 更新我们创建的 GitHub 库已经更新,可以在使用 iOS 7.0.3 时稍微解决这个问题。不幸的是,没有神奇的公式来支持在 iOS 7.0.2 及更早版本和 iOS 7.0.3 中创建的两种颜色。似乎Apple提高了饱和度,但以不透明度为代价(因为模糊的半透明取决于不透明度级别)。我和其他一些人正在努力为此创建一个更好的解决方案。


我相信很多人已经遇到过这样的问题:iOS 7 倾向于降低半透明 UINavigationBar 的颜色饱和度。

我的目标是实现一个具有这种色调但半透明的 UINavigationBar:

UINavigationBar,不透明

但是,通过半透明,我得到了这个。背景视图是白色的,我知道这会使这个视图更亮一点:

UINavigationBar,半透明

有没有办法在保持半透明的同时达到原始颜色?我注意到 Facebook 已经能够让他们的栏变成丰富的蓝色,如下所示:

Facebook UINavigationBar,半透明

..所以我知道必须有某种方式。背景视图显然在这里有所作为,但它们的大部分内容也是灰色/白色的。似乎无论您输入哪种条形色调颜色,您都无法在半透明下获得鲜艳的色彩。

更新了解决方案。

这是我最终提出的解决方案。我采用了aprato的解决方案,然后将自定义包含UINavigationBar在一个UINavigationController子类中。我创建了一个存储库,该存储库具有下面列出的此实现以及示例应用程序

////////////////////////////
// CRNavigationBar.m
////////////////////////////

#import "CRNavigationBar.h"

@interface CRNavigationBar ()
@property (nonatomic, strong) CALayer *colorLayer;
@end

@implementation CRNavigationBar

static CGFloat const kDefaultColorLayerOpacity = 0.5f;
static CGFloat const kSpaceToCoverStatusBars = 20.0f;

- (void)setBarTintColor:(UIColor *)barTintColor {
    [super setBarTintColor:barTintColor];
    if (self.colorLayer == nil) {
        self.colorLayer = [CALayer layer];
        self.colorLayer.opacity = kDefaultColorLayerOpacity;
        [self.layer addSublayer:self.colorLayer];
    }
    self.colorLayer.backgroundColor = barTintColor.CGColor;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    if (self.colorLayer != nil) {
        self.colorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars);

        [self.layer insertSublayer:self.colorLayer atIndex:1];
    }
}

@end

////////////////////////////
// CRNavigationController.m
////////////////////////////

#import "CRNavigationController.h"
#import "CRNavigationBar.h"

@interface CRNavigationController ()

@end

@implementation CRNavigationController

- (id)init {
    self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil];
    if(self) {
        // Custom initialization here, if needed.    
    }
    return self;
}

- (id)initWithRootViewController:(UIViewController *)rootViewController {
    self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil];
    if(self) {
        self.viewControllers = @[rootViewController];
    }

    return self;
}

@end
4

16 回答 16

52

iOS 7.0.3 更新:正如你在上面看到的 7.0.3 改变了一些事情。我已经更新了我的要点。希望随着人们的升级,这种情况会消失。

原始答案: 我最终得到了一个结合其他两个答案的黑客。我将 UINavigationBar 子类化并在后面添加一个图层,如果各种高度状态栏中的任何一个处于上升状态,则可以使用一些额外的空间来覆盖。图层在布局子视图中进行调整,并且每当您设置 barTintColor 时颜色都会发生变化。

要点:https ://gist.github.com/aprato/6631390

setBarTintColor

  [super setBarTintColor:barTintColor];
  if (self.extraColorLayer == nil) {
    self.extraColorLayer = [CALayer layer];
    self.extraColorLayer.opacity = self.extraColorLayerOpacity;
    [self.layer addSublayer:self.extraColorLayer];
  }
  self.extraColorLayer.backgroundColor = barTintColor.CGColor;

布局子视图

  [super layoutSubviews];
  if (self.extraColorLayer != nil) {
    [self.extraColorLayer removeFromSuperlayer];
    self.extraColorLayer.opacity = self.extraColorLayerOpacity;
    [self.layer insertSublayer:self.extraColorLayer atIndex:1];
    CGFloat spaceAboveBar = self.frame.origin.y;
    self.extraColorLayer.frame = CGRectMake(0, 0 - spaceAboveBar, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + spaceAboveBar);
  }
于 2013-09-19T23:47:36.357 回答
10

iOS 7.0 中条形的 tintColor 行为已更改。它不再影响栏的背景,其行为与添加到 UIView 的 tintColor 属性的描述相同。要为栏的背景着色,请使用 -barTintColor。您可以使用以下代码使应用程序同时适用于 ios6 和 ios7。

if(IS_IOS7)
{
    self.navigationController.navigationBar.barTintColor = [UIColor blackColor];
    self.navigationController.navigationBar.translucent = NO;
}
else
{
    self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}

IS_IOS7 是一个宏,在 pch 文件中定义如下。

#define IS_IOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
于 2013-12-27T04:29:12.793 回答
9

我没有想出这个解决方案,但它似乎工作得很好。我刚刚将它添加到 UINavigationController 子类的 viewDidLoad 中。

来源:https ://gist.github.com/alanzeino/6619253

// cheers to @stroughtonsmith for helping out with this one

UIColor *barColour = [UIColor colorWithRed:0.13f green:0.14f blue:0.15f alpha:1.00f];
UIView *colourView = [[UIView alloc] initWithFrame:CGRectMake(0.f, -20.f, 320.f, 64.f)];
colourView.opaque = NO;
colourView.alpha = .7f;
colourView.backgroundColor = barColour;
self.navigationBar.barTintColor = barColour;
[self.navigationBar.layer insertSublayer:colourView.layer atIndex:1];
于 2013-09-19T21:21:24.877 回答
6

一种低保真方法可能是将UIView导航栏的高度固定到导航栏后面的视图顶部。使该视图与导航栏颜色相同,但使用 alpha 直到获得所需的效果:

UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.navigationController.navigationBar.frame), 64)];
    backgroundView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1 alpha:.5];

[self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];

UIView 后面

在此处输入图像描述

(将颜色从较低的示例更改为强调透明度。透明度/模糊在移动时更加明显。)

子类化UINavigationBar并将相同的视图放置在背景之上但在其他所有内容之后可能会获得类似的结果,同时不那么hacky。


我见过的另一个解决方案是玩 alpha 的UINavigationBar

self.navigationController.navigationBar.alpha = 0.5f;

编辑:实际上,经过测试,这似乎没有提供预期的行为(或任何行为):

.8 阿尔法

带有 0.8 alpha 的导航栏

未调整的阿尔法

在此处输入图像描述

显然,您只想在 iOS 7 设备上执行此操作。因此,在实施任何这些之前添加一些版本检查。

于 2013-09-19T14:35:08.907 回答
4

不要以RGB格式创建 UIColor 对象,而是使用HSB并增加饱和度参数。(感谢Sam Soffes在此处描述此方法)

navigationBar.barTintColor = [UIColor colorWithHue:0.555f saturation:1.f brightness:0.855f alpha:1.f];

注意:此解决方案是一种折衷方案,不适用于高饱和度的颜色。

要从您的设计中选择 HSB 颜色,您可以使用ColorSnapper 之类的工具,它允许您简单地复制 UIColor HSB 格式。

您还可以尝试使用David Keegan的 UIColor 类别( GitHub 链接)来修改现有颜色。

于 2013-10-15T14:16:18.597 回答
3

Apple 已在新的 7.0.3 版本中修复了该问题。

于 2013-10-23T09:36:48.067 回答
1

我使用了@aprato 的解决方案,但发现了一些极端情况,其中来自新 VC 的新层(例如UINavigationItemButtonViewsUINavigationItemViews等)会自动插入到下方的位置extraColorLayer(这会导致这些标题或按钮元素受到影响,extraColorLayer因此颜色比正常情况要暗)。所以我调整了@aprato 的解决方案以强制extraColorLayer保持在索引位置 1。在索引位置 1 处,extraColorLayer保持在 的正上方_UINavigationBarBackground,但在其他所有内容的下方。

这是我的类实现:

- (void)setBarTintColor:(UIColor *)barTintColor
{
    [super setBarTintColor:barTintColor];
    if (self.extraColorLayer == nil)
    {
        self.extraColorLayer = [CALayer layer];
        self.extraColorLayer.opacity = kDefaultColorLayerOpacity;
        [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
    }
    self.extraColorLayer.backgroundColor = barTintColor.CGColor;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    if (self.extraColorLayer != nil)
    {
        self.extraColorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars);
    }
}

- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview
{
    [super insertSubview:view aboveSubview:siblingSubview];
    [self.extraColorLayer removeFromSuperlayer];
    [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}

- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
{
    [super insertSubview:view atIndex:index];
    [self.extraColorLayer removeFromSuperlayer];
    [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}

- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview
{
    [super insertSubview:view belowSubview:siblingSubview];
    [self.extraColorLayer removeFromSuperlayer];
    [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}
于 2013-09-24T18:55:52.053 回答
1

我已经在我的 fork 中改进了您的代码:https ://github.com/allenhsu/CRNavigationController

通过我的修改,屏幕上的结果颜色(在白色背景上选择)将与传入的值完全相同setBarTintColor。我认为这是一个了不起的解决方案。

于 2013-10-23T06:48:53.097 回答
0

这些黑客都不是必需的:)。简单设置:

self.navigationController.navigationBar.translucent = NO;

对于 iOS 7,默认的半透明保持为 TRUE。

于 2013-09-21T08:26:02.877 回答
0

在相关说明中,您可以通过以下方式轻松设置标题文本颜色(带阴影):

NSShadow *titleShadow = [[NSShadow alloc] init];
titleShadow.shadowOffset = CGSizeMake(0.0f, -1.0f);
titleShadow.shadowColor = [UIColor blackColor];
NSDictionary *navbarTitleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor],
                                            NSShadowAttributeName: titleShadow};
[[UINavigationBar appearance] setTitleTextAttributes:navbarTitleTextAttributes];
于 2013-09-26T21:19:45.550 回答
0

我在尝试设置一个统一颜色的导航栏时遇到了这个 Q/A,在 iOS 7 上禁用了透明度。

在用 barTintColor 试验了一段时间后,我发现拥有不透明导航栏的一种非常简单的方法是制作所需颜色的单像素图​​像,从中制作可拉伸的图像,并将其设置为导航栏的背景图像.

UIImage *singlePixelImage = [UIImage imageNamed:@"singlePixel.png"];
UIImage *resizableImage = [singlePixelImage resizableImageWithCapInsets:UIEdgeInsetsZero];
[navigationBar setBackgroundImage:resizableImage forBarMetrics:UIBarMetricsDefault]; 

三行代码,非常简单,适用于 iOS 6 和 iOS 7(iOS 6 不支持 barTintColor)。

于 2013-10-11T15:34:04.540 回答
0

Simon Booth 提供了一个很棒的 Dropin UINavigationController 替代品,可在 GitHub Here GitHub - C360NavigationBar

如果您向后支持 iOS6,请检查根视图控制器,如下所示:

PatientListTableViewController *frontViewController = [[PatientListTableViewController alloc] init];

    UINavigationController *navViewController = [[UINavigationController alloc] initWithNavigationBarClass:[C360NavigationBar class] toolbarClass:nil];
if ([navViewController.view respondsToSelector:@selector(setTintColor:)]) {
    //iOS7
    [navViewController.view setTintColor:self.navBarTintColor];
    [[C360NavigationBar appearance] setItemTintColor:self.navBarItemTintColor];
} else {
    //iOS6
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque animated:NO];
    navViewController.navigationBar.tintColor = self.navBarTintColor;
}
[navViewController pushViewController:frontViewController animated:NO];

self.window.rootViewController = navViewController;
于 2013-10-23T18:42:15.477 回答
0

正如上面提到的@bernhard ,可以使条形颜色饱和以获得所需的导航栏外观。

我为这种调整编写了一个BarTintColorOptimizer 实用程序。它优化了半透明条的色调颜色,使条的实际颜色与 iOS 7.x 及更高版本中所需的颜色相匹配。查看此答案以了解详细信息。

于 2015-11-24T18:06:34.110 回答
-1

有没有办法在不继承 UINavigationBar 的情况下使用 @aprato 解决方案。

在我的项目中,我的主视图是 UIViewController。

问题是navigationController是一个只读属性,有没有办法在我的项目中使用你的类,因为我不能使用:[[UINavigationController alloc] initWithNavigationBarClass:

谢谢

于 2013-09-29T10:16:55.900 回答
-1

坦率地说,以上答案可能是正确的,但以下技巧对我来说很容易。

// this is complete 100% transparent image
self.imageBlack = [[UIImage imageNamed:@"0102_BlackNavBG"] 
           resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)  
                          resizingMode:UIImageResizingModeStretch];

// this is non-transparent but iOS7 
// will by default make it transparent (if translucent is set to YES)
self.imageRed = [[UIImage imageNamed:@"0102_RedNavBG"] 
         resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)  
                        resizingMode:UIImageResizingModeStretch];

// some navigation controller
[nvCtrLeft.navigationBar setBackgroundImage:self.imageRed 
                              forBarMetrics:UIBarMetricsDefault];

// some another navigation controller
[nvCtrCenter.navigationBar setBackgroundImage:self.imageRed 
                                forBarMetrics:UIBarMetricsDefault];

这是用于self.imageRed和的图像self.imageBlack

< 自我形象黑色> 黑色图像在这个括号中,因为它是透明的,所以不可见:)

< 自我形象红> 红色图像在这个括号中。

于 2014-08-07T15:44:39.077 回答
-2

获得所需颜色的一种简单方法是使用

    [<NAVIGATION_BAR> setBackgroundImage:<UIIMAGE> forBarPosition:<UIBARPOSITION> barMetrics:<UIBARMETRICS>];

只要您的图像有一些 alpha,半透明就会起作用,您可以通过更改图像来设置 alpha。这只是在 iOS7 中添加的。图像的宽度和高度是垂直的 640x88 像素(如果您希望它位于状态栏下方,则将 20 添加到 88)。

于 2013-09-20T23:46:37.417 回答