Достижение ярких, ярких цветов для полупрозрачного UINavigationBar iOS 7


iOS 7.1 UPDATE. Похоже, что обходной путь для изменения альфа-канала в UINavigationBar был проигнорирован в этом обновлении. Прямо сейчас лучшим решением является просто "справиться с этим" и надеяться, что любой цвет, который вы выберете, может сделать полупрозрачный эффект. Я все еще ищу способы обойти это.


iOS 7.0.3 UPDATE: Библиотека GitHub, которую мы создали, была обновлена, чтобы немного обойти эту проблему при использовании iOS 7.0.3. К сожалению, нет волшебной формулы для поддержки обоих цветов, созданных в iOS 7.0.2 и более ранних версиях, и iOS 7.0.3. Похоже, что Apple улучшило насыщенность, но ценой непрозрачности (поскольку размытая полупрозрачность зависит от уровня непрозрачности). Я, наряду с несколькими другими, работает над созданием гораздо лучшего решения для этого.


Я уверен, что многие люди уже столкнулись с проблемой, когда iOS 7 имеет тенденцию обесцветить цвет UINavigationBar, который является полупрозрачным.

Моя цель - добиться UINavigationBar с этим цветом оттенка, но полупрозрачным:

UINavigationBar, Opaque

Однако, с прозрачностью, я получаю это. Фоновый вид белого цвета, который, как я понимаю, сделает это представление немного легче:

UINavigationBar, Translucent

Есть ли какой-либо способ добиться первоначального цвета при все еще прозрачности? Я заметил, что Facebook смог получить свой бар, чтобы быть его богатым, синим цветом, как показано здесь:

Facebook UINavigationBar, Translucent

.. поэтому я знаю, что должен быть какой-то способ. Фоновые представления, очевидно, имеют здесь значение, но большая часть их содержимого также серая/белая. Кажется, что независимо от того, какой цвет оттенка штриха вы вставляете, вы не можете получить яркие цвета при прозрачности.

Обновлено с помощью решения.

Вот решение, с которым я закончил. Я принял решение 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

Ответ 1

iOS 7.0.3 UPDATE: Как вы видите выше, 7.0.3 изменили ситуацию. Я обновил свой смысл. Надеюсь, это просто исчезнет, ​​когда люди обновятся.

Исходный ответ: Я закончил с хаком, совмещающим два других ответа. Я подклассифицирую UINavigationBar и добавляю слой в спину с некоторым дополнительным пространством, чтобы покрыть, если какая-либо из различных баров состояния по высоте. Слой корректируется в подзонах компоновки, а цвет меняется всякий раз, когда вы устанавливаете barTintColor.

Gist: 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;

layoutSubviews

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

Ответ 2

Поведение tintColor для баров изменилось на iOS 7.0. Он больше не влияет на фоновый фон и ведет себя так, как описано для свойства tintColor, добавленного в UIView. Чтобы подчеркнуть фоновый фон, используйте -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)

Ответ 3

Я не придумал это решение, но, похоже, он работает неплохо. Я просто добавил его в viewDidLoad в мой подкласс UINavigationController.

Источник: 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];

Ответ 4

Один низкоуровневый способ, вероятно, будет закреплять UIView, который является высотой панели навигации в верхней части представления за панелью. Сделайте этот вид того же цвета, что и панель навигации, но играйте с альфой, пока не получите желаемые эффекты:

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:

self.navigationController.navigationBar.alpha = 0.5f;

Забастовкa > Изменить: На самом деле, после тестирования кажется, что это не обеспечивает поведения (или любого поведения):

.8 alpha

Navigation bar with .8 alpha

Неадаптированная альфа

enter image description here

Очевидно, что вы захотите сделать это только на устройствах iOS 7. Итак, добавьте некоторую проверку версии, прежде чем внедрять какие-либо из них.

Ответ 5

Вместо создания вашего объекта UIColor в формате RGB используйте HSB и увеличьте параметр насыщения. (Кредиты Sam Soffes, который описывает этот метод здесь)

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

Примечание. Это решение является компромиссом и не подходит для цветов с высокой насыщенностью.

Чтобы выбрать цвет HSB из вашего дизайна, вы можете использовать инструмент, например ColorSnapper, который позволяет просто скопировать формат UIColor HSB.

Вы также можете попробовать категорию UIColor (ссылка GitHub) из David Keegan, чтобы изменить существующие цвета.

Ответ 6

Теперь проблема исправлена ​​Apple в новой версии 7.0.3.

Ответ 7

Я использовал решение @aprato, но нашел несколько угловых случаев, когда новые слои из новых VC (например, UINavigationItemButtonViews, UINavigationItemViews и т.д.) будут автоматически вставлены в позицию ниже 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
}

Ответ 8

Я улучшил свой код в своей вилке: https://github.com/allenhsu/CRNavigationController

С моей модификацией, цвет результата на экране (выбранный на белом фоне) будет точно таким же значением, переданным в setBarTintColor. Я считаю это удивительным решением.

Ответ 9

Ни один из этих хаков не требуется:). Просто установите:

self.navigationController.navigationBar.translucent = NO;

Для iOS 7, по умолчанию полупрозрачность хранится в TRUE.

Ответ 10


В соответствующей заметке вы можете легко настроить цвет текста заголовка (с тенью) с помощью:

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

Ответ 11

Я столкнулся с этим вопросом Q/A, пытаясь настроить равномерно окрашенную навигационную панель с прозрачностью DISABLED на iOS 7.

Проведя некоторое время с помощью barTintColor, я понял, что очень простой способ иметь непрозрачную навигационную панель состоит в том, чтобы сделать одно пиксельное изображение желаемого цвета, сделать из него растягиваемое изображение и установить его на backgroundImage панель навигации.

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

Три строки кода, очень простые и работают на iOS 6 и iOS 7 (barTintColor не поддерживается на iOS 6).

Ответ 12

Theres отличная замена смены UINavigationController, доступная от Simon Booth, доступная в GitHub Здесь 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;

Ответ 13

Как @bernhard упомянутый выше, можно насытить цвет оттенка бара, чтобы получить желаемый вид навигационной панели.

Я написал утилиту BarTintColorOptimizer для такой настройки. Он оптимизирует полупрозрачный цвет оттенков штрихов, чтобы фактический цвет панели соответствовал желаемому цвету в iOS 7.x и более поздних версиях. Подробнее см. этот ответ.

Ответ 14

Есть ли способ использовать решение @aprato без подкласса UINavigationBar.

В моем проекте мое основное представление - UIViewController.

проблема в том, что navigationController является свойством readonly, есть ли способ использовать ваш класс с моим проектом, потому что я не могу использовать: [[UINavigationController alloc] initWithNavigationBarClass:

спасибо

Ответ 15

Откровенно говоря, выше ответы могут быть правильными, но следующий трюк работал у меня с очень легкостью.

// 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.

< self.imageBlack > черное изображение в этих скобках не будет видно, поскольку оно прозрачно:)

< self.imageRed > В этих скобках находится красное изображение.

Ответ 16

Легкий способ получить нужный вам цвет:

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

Пока у вашего изображения есть альфа, полупрозрачность будет работать, и вы можете установить альфа, изменив изображение. Это было просто добавлено в iOS7. Ширина и высота изображения равны 640x88px для вертикали (добавьте 20 к 88, если вы хотите, чтобы они находились под строкой состояния).