为 iOS7半透明 UINavigationBar 实现明亮、生动的色彩


IOS 7.1 UPDATE : 看起来修改 UINavigationBar 中 alpha 通道的解决方案在这次更新中被忽略了。现在,最好的解决办法似乎就是“处理它”,并希望无论你选择什么颜色都能呈现出半透明的效果。我还在想办法解决这个问题。


IOS7.0.3更新 : 在使用 iOS7.0.3时,我们创建的 GitHub 库已经进行了更新,略微解决了这个问题。不幸的是,没有一个神奇的公式能同时支持 iOS7.0.2及更早版本和 iOS7.0.3中创建的两种颜色。看起来苹果提高了饱和度,但是以不透明度为代价(因为模糊的半透明度取决于不透明度水平)。我和其他一些人正在努力为此创造一个更好的解决方案。


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

我的目标是实现一个 UINavigationBar,颜色是这样的,但是是半透明的:

UINavigationBar, Opaque

然而,通过半透明,我得到了这个。背景图是白色的,我知道这会让这个图看起来更亮一些:

UINavigationBar, Translucent

有没有办法在保持半透明的情况下达到原色?我已经注意到 Facebook 已经能够让他们的酒吧成为他们丰富的蓝色,如下图所示:

Facebook UINavigationBar, Translucent

..所以我知道一定有办法。背景视图在这里显然有所不同,但是它们的大部分内容也是灰色/白色的。似乎无论你加入什么条形色彩,你都无法在半透明状态下获得生动的色彩。

更新了解决方案。

这是我最后想到的解决办法。我采用了 慢慢来的解决方案,然后在 UINavigationController子类中包含了定制的 UINavigationBar我已经创建了一个存储库,下面列出了这个实现以及一个示例应用程序.

////////////////////////////
// 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
49953 次浏览

一种低保真的方法可能是将导航条的高度 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 落后

enter image description here

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

UINavigationBar进行子类化,将同样的视图放在背景之上,但是放在其他所有背景之后,可能会得到类似的结果,同时不那么粗糙。


我看到的另一个解决方案是使用 UINavigationBar的 alpha:

self.navigationController.navigationBar.alpha = 0.5f;

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

.8 Alpha

Navigation bar with .8 alpha

未经调整的阿尔法

enter image description here

显然,您只想在 iOS7设备上执行此操作。

我没有想出这个解决方案,但它似乎工作得相当不错。我只是将它添加到 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];

IOS7.0.3更新: 正如你在7.0.3上看到的那样,3改变了一些事情。我更新了我的要点。希望随着人们的升级,这种情况会消失。

原答案: 最后我把其他两个答案混合起来。我的子类 UINavigationBar 和添加一个图层的背面与一些额外的空间来覆盖,如果任何各种高度状态栏是上升的。图层在布局子视图中进行调整,并且每当设置 barTintColor 时颜色都会发生变化。

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

色彩

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

获得您想要的颜色的一个简单方法是使用

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

只要你的图像有一些阿尔法,半透明将工作,你可以设置通过改变图像的阿尔法。这是刚刚在 iOS7中加入的。图像的宽度和高度为640x88px (如果您希望它位于状态栏的下面,则在88后面加上20)。

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

self.navigationController.navigationBar.translucent = NO;

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

我使用了@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
}

另外,你可以通过以下方式轻松设置标题文字的颜色(带阴影) :

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

有没有一种不用子类化 UINavigationBar 就可以使用@aprato 解决方案的方法。

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

问题是,导航控制器是一个只读属性,有没有一种方法可以使用你的类与我的项目,因为我不能使用: [[UINavigationController alloc] initWithNavigationBarClass:

谢谢

当我试图在 iOS7上设置一个透明禁用的统一颜色的导航栏时,遇到了这个问题。

在对 barTintColor 进行了一段时间的实验后,我发现一个非常简单的方法就是使用一个不透明的导航栏,制作一个想要的颜色的单个像素图像,用它制作一个可伸缩的图像,然后将它设置为背景图像。

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

三行代码,非常简单,可以在 iOS6和 iOS7上同时工作(iOS6不支持 barTintColor)。

不要使用 RGB格式创建 UIColor 对象,而是使用 汇丰银行并增加饱和度参数。(感谢描述此方法的 山姆 · 索菲斯)

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

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

要从设计中选择 HSB 颜色,您可以使用像 彩虹鲷这样的工具,它允许您简单地复制 UIColor HSB 格式。

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

我在我的分叉中改进了你的代码: https://github.com/allenhsu/CRNavigationController

通过我的修改,屏幕上的结果颜色(选择白色背景)将完全相同的值传递到 setBarTintColor。我觉得这是个很棒的解决方案。

这个问题现在已经被苹果公司在新的7.0.3版本中解决了。

有一个伟大的 Dropin UINavigationController 更换可从西蒙布斯可在 GitHub Here GitHub-C360NavigationBar

如果你反向支持 iOS6,那么检查一下 root 视图控制器:

PatientListTableViewController * front ViewController = [[ 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;

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

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

// 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.imageRedself.imageBlack的图像。

黑色的图像在这个括号中是不可见的,因为它是透明的:)

红色图片在括号内。

作为@bernhard 上面提到的,可以饱和条形图色以获得所需的导航条外观。

我为这种调整写了一个 色彩优化工具。它优化了半透明条形色调颜色,使条形色调与 iOS 7.x 及更高版本中所需的颜色相匹配。详情请参阅 这个答案