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


172

ОБНОВЛЕНИЕ iOS 7.1 : похоже, что временное решение для изменения альфа-канала в UINavigationBar было проигнорировано в этом обновлении. Прямо сейчас, лучшее решение, кажется, состоит в том, чтобы просто «справиться с этим» и надеяться, что любой выбранный вами цвет может оказать полупрозрачный эффект. Я все еще ищу способы обойти это.


ОБНОВЛЕНИЕ 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

Facebook iOS7 UINAvigationBarнепрозрачен?
Vinzzz

Нет, это намного более тонкая прозрачность, чем стандартная iOS. Гораздо лучше, ИМО.
Кевин

Facebook NavigationBar не прозрачный
LE SANG

7
Это определенно полупрозрачный; пожалуйста, смотрите мой отредактированный ответ.
SpacePyro

2
@Odelya - Это не решение для получения правильных цветов, а скорее решение для UINavigationBarмаксимально возможной коррекции яркости при воздействии прозрачности в iOS 7.
SpacePyro

Ответы:


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;

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

Где вы реализовали это на ViewController? Под viewDidLoad?
Джереми

Это прекрасно работает! Я решил добавить это в свой UINavigationControllerподкласс. Вот суть для тех, кто заинтересован в наличии обычая UINavigationBarв UINavigationController.
SpacePyro

@ Джереми, как SpacePyro У меня есть подкласс контроллера навигации (который похож на бар, который я использовал до 7 для таргетинга UIAppearance), который переопределяет, initWithRootViewControllerчтобы использовать это. Я обновил свою суть
Энтони

Хм ... ладно ... Я думаю, мне нужно увидеть это во всем тестовом проекте, чтобы полностью понять, что вы имеете в виду. Я смог воспроизвести пример timeuser ниже, но я все еще немного зелен с этим.
Джереми

1
@ Mr.T Спасибо! Когда я писал, это еще не выдвигалось :) Но я обновил его сейчас, добавил UIAppearance для прозрачности и поддержки NSCoding
Anthony

10

Поведение 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)

9

Я не придумал это решение, но, похоже, оно работает довольно хорошо. Я только добавил это к 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];

1
Похоже, что после добавления нового контроллера просмотра на контроллер навигации все элементы панели кнопок перемещаются, чтобы оказаться под этим слоем прозрачности.
Эван Дэвис,

Заголовки также скрываются этим прозрачным слоем.
Ник Локинг

Я написал эту суть. Комментарии верны; либо элементы кнопки помещаются внизу при нажатии нового контроллера представления, либо подслои наследуют альфа из colourView. Я раздвоил другой ответ и исправил несколько вещей в новом уроке: github.com/alanzeino/AZColoredNavigationBar
Алан Зейно

Я смог поставить текст белым, выбрав стиль «Полупрозрачная черная навигационная панель» ... это помогло мне. Также установите yourBar.tintColor = [UIColor whiteColor]; для других пользовательских кнопок сверху.
Хуан Замора

6

Вероятно, одним из способов низкоуровневой 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 позади

введите описание изображения здесь

(Изменен цвет с более низких примеров на прозрачность выделения. Прозрачность / размытие более заметны при движении.)

Создание подклассов UINavigationBarи размещение того же представления над фоном, но за всем остальным, вероятно, приведет к аналогичным результатам, но при этом будет менее хакерским.


Другое решение, которое я видел, это игра с альфа-версией UINavigationBar:

self.navigationController.navigationBar.alpha = 0.5f;

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

.8 альфа

Панель навигации с .8 альфа

Нескорректированная альфа

введите описание изображения здесь

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


Я придерживаюсь подхода обновления альфы и tintColor, чтобы получить цвет, который вы ищете.
StuartM

Если вы можете получить желаемый эффект этим менее агрессивным способом, то полностью. Но я не уверен, насколько это будет эффективно.
Кевин

Единственная проблема, с которой я сталкиваюсь (и она, кажется, не представлена ​​на вашем рисунке), заключается в том, что она добавляет вид непосредственно сверху панели навигации, и в результате этого блокируются элементы управления: cl.ly / image / 3d3C2o0N283S
SpacePyro

Ой, теперь я вижу. Должно быть[self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];
Кевин

Да, я понял, что ты это имел ввиду. ;) Это определенно дает мне лучший диапазон цветов для выбора. Получить вибрацию исходного цвета, вероятно, будет невозможно, учитывая, что эффект размытия все еще действительно нейтрализует что-либо под ним. Мне просто придется жить с более темным цветом на данный момент.
SpacePyro

4

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

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

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

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

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


3

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


1
Да, их реализация цвета оттенка определенно изменилась в 7.0.3. Теперь я могу добиться желаемого цвета, просто увеличив насыщенность примерно на 10-15%. Прежде мне также нужно было рекламировать дополнительный слой.
Матей Буковински

Спасибо, что дал мне знать! Я буду загружать обновление для моей библиотеки для этого.
SpacePyro

1
И нефиксированный в 7.1> _ <
Лео Натан

1

Я использовал решение @ 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
}

Так как слои и подпредставления не смешаны, мне не нужно перегружать addSubView
Роб ван дер Веер

1

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

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


@JordanBrown не тестировался на 7.1 и 8
Аллен Сюй

0

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

self.navigationController.navigationBar.translucent = NO;

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


17
Но вопрос требует и прозрачности.
Лео Натан

:) Спасибо, у меня это сработало. Однако под навигационной панелью есть черная нижняя граница 1px. Есть ли способ удалить это?
Притеш Десаи

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

1
Если вы пытаетесь соответствовать стилю iOS 7, использование тени на заголовке текста, вероятно, неуместно.
Ник Локинг,

Вы правы, в iOS 7 Apple больше не устанавливает тени на тексте своей навигационной панели. Если вы хотите, чтобы пользовательский взгляд, хотя, это даст вам возможность.
bryguy1300

0

Я столкнулся с этим вопросом, пытаясь настроить однородно окрашенную панель навигации с отключенной прозрачностью на 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).


0

Вот отличная замена Dropin UINavigationController от Саймона Бута, доступная на 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;

Если я что-то упустил, бары этой библиотеки не являются полупрозрачными.
Ривера

0

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

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


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

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

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


Это не дает вам размытия.
Ортвин Генц

-1

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

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

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

Спасибо


Не могли бы вы просто обернуть себя UIViewControllerвнутри UINavigationControllerс [[[UINavigationController alloc] initWithRootViewController:<YourViewController>]? На этот вопрос также лучше ответить отдельным вопросом, чем размещать его здесь.
SpacePyro

-2

Простой способ получить нужный цвет - это использовать

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

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


Это не сохраняет размытие, однако. Есть прозрачность, но нет размытия.
Лео Натан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.