Uzyskanie jasnych, żywych kolorów dla półprzezroczystego paska UINavigationBar systemu iOS 7

172

AKTUALIZACJA iOS 7.1 : Wygląda na to, że obejście zmiany kanału alfa na pasku UINavigationBar zostało zignorowane w tej aktualizacji. W tej chwili najlepszym rozwiązaniem wydaje się po prostu „uporać się z tym” i mieć nadzieję, że wybrany kolor da efekt półprzezroczystości. Nadal szukam sposobów obejścia tego.


AKTUALIZACJA iOS 7.0.3 : Biblioteka GitHub, którą utworzyliśmy , została zaktualizowana, aby nieco obejść ten problem podczas korzystania z systemu iOS 7.0.3. Niestety nie ma magicznej formuły obsługującej oba kolory stworzone w iOS 7.0.2 i wcześniejszych oraz iOS 7.0.3. Wygląda na to, że Apple poprawiło nasycenie, ale kosztem krycia (ponieważ rozmyta przezroczystość zależy od poziomu krycia). Wraz z kilkoma innymi pracujemy nad stworzeniem znacznie lepszego rozwiązania tego problemu.


Jestem pewien, że wiele osób już spotkało się z problemem, w którym iOS 7 ma tendencję do desaturacji koloru paska UINavigationBar, który jest półprzezroczysty.

Moim celem jest osiągnięcie UINavigationBar z tym odcieniem koloru, ale półprzezroczystym:

UINavigationBar, nieprzezroczysty

Jednak dzięki przezroczystości dostaję to. Widok tła jest biały, co, jak rozumiem, sprawi, że ten widok będzie nieco jaśniejszy:

UINavigationBar, Translucent

Czy istnieje sposób na uzyskanie oryginalnego koloru przy zachowaniu przezierności? Zauważyłem, że Facebookowi udało się uzyskać ich pasek, aby był ich bogatym, niebieskim kolorem, jak pokazano tutaj:

Facebook UINavigationBar, półprzezroczysty

.. więc wiem, że musi być jakiś sposób. Widoki w tle oczywiście mają tutaj znaczenie, ale większość ich treści jest również szaro-biała. Wygląda na to, że niezależnie od zastosowanego odcienia paska, pod przezroczystością nie można uzyskać żywych kolorów.

Zaktualizowany rozwiązaniem.

Oto rozwiązanie, które wymyśliłem. Wziąłem aprato rozwiązanie „s, a następnie objęła zwyczaj UINavigationBarobrębie UINavigationControllerpodklasy. Utworzyłem repozytorium, które ma tę implementację wymienioną poniżej, wraz z przykładową aplikacją .

////////////////////////////
// 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
SpacePyro
źródło
czy Facebook iOS7 nie jest UINAvigationBarnieprzezroczysty?
Vinzzz
Nie, jest to znacznie bardziej subtelna przezroczystość niż domyślny iOS. Znacznie lepiej, IMO.
Kevin
Pasek
7
Jest zdecydowanie przezroczysty; proszę zobaczyć moją zredagowaną odpowiedź.
SpacePyro,
2
@Odelya - To nie jest rozwiązanie pozwalające uzyskać poprawne kolory, ale raczej rozwiązanie poprawiające jasność UINavigationBarjak najlepiej po wystawieniu na półprzezroczystość w iOS 7.
SpacePyro

Odpowiedzi:

52

AKTUALIZACJA iOS 7.0.3: Jak widać powyżej 7.0.3 zmienił rzeczy. Zaktualizowałem treść. Miejmy nadzieję, że to zniknie, gdy ludzie dokonają aktualizacji.

Oryginalna odpowiedź: Skończyło się na hackowaniu dwóch pozostałych odpowiedzi. Podklasuję UINavigationBar i dodaję warstwę z tyłu z dodatkową przestrzenią do zakrycia, jeśli którykolwiek z pasków stanu różnych wysokości jest włączony. Warstwa jest dostosowywana w podglądzie układu, a kolor zmienia się po każdym ustawieniu barTintColor.

Streszczenie: 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);
  }
Anthony
źródło
Gdzie zaimplementowałeś to w ViewController? Under viewDidLoad?
Jeremy,
To działa pięknie! Postanowiłem dodać to do mojej UINavigationControllerpodklasy. Oto podsumowanie tego dla tych, którzy są zainteresowani posiadaniem zwyczaju UINavigationBarw UINavigationController.
SpacePyro
@Jeremy jak SpacePyro Mam podklasę kontrolera nawigacji (która podobnie jak pasek, którego używałem przed 7 do kierowania na UIAppearance), która zastępuje initWithRootViewControllerto. Zaktualizowałem moją treść
Anthony
Um… okej… Myślę, że muszę zobaczyć to w całym projekcie testowym, aby w pełni zrozumieć, co masz na myśli. Udało mi się odtworzyć przykład użytkownika czasu poniżej, ale nadal jestem trochę zielony z tym.
Jeremy,
1
@ Mr.T Dzięki! Kiedy pisałem, to jeszcze nie pchałem :) Ale zaktualizowałem go teraz, dodałem UIAppearance dla krycia i wsparcia dla NSCoding
Anthony
10

Zachowanie tintColor dla pasków zmieniło się w systemie iOS 7.0. Nie wpływa już na tło paska i zachowuje się tak, jak opisano dla właściwości tintColor dodanej do UIView. Aby odcień tła paska, użyj -barTintColor. Możesz użyć następującego kodu, aby aplikacja działała zarówno z ios6, jak i ios7.

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

IS_IOS7 to makro zdefiniowane w pliku PCH w następujący sposób.

#define IS_IOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
Bhavesh
źródło
9

Nie wymyśliłem tego rozwiązania, ale wydaje się, że działa całkiem dobrze. Właśnie dodałem go do viewDidLoad w mojej podklasie UINavigationController.

Źródło: 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];
Jamie Hamick
źródło
1
Wygląda na to, że po umieszczeniu nowego kontrolera widoku na kontrolerze nawigacji wszystkie elementy przycisków paska zostaną przeniesione pod tę warstwę przezroczystości.
Evan Davis,
Tytuły są również zasłaniane przez tę warstwę przezroczystości.
Nick Locking
Napisałem to streszczenie. Komentarze są słuszne; albo elementy przycisku są wypychane pod spodem, gdy zostanie wciśnięty nowy kontroler widoku, albo podwarstwy dziedziczą wartość alfa colourView. Rozwidliłem drugą odpowiedź i naprawiłem kilka rzeczy w nowej kropli w klasie: github.com/alanzeino/AZColoredNavigationBar
Alan Zeino
Udało mi się umieścić tekst na biało, wybierając styl Przezroczysty czarny pasek nawigacyjny ... to załatwiło sprawę. Ustaw także yourBar.tintColor = [UIColor whiteColor]; dla innych niestandardowych przycisków na górze.
Juan Zamora
6

Jednym ze sposobów low-fi prawdopodobnie byłoby przypięcie UIViewwysokości paska nawigacji do górnej części widoku za paskiem. Ustaw ten widok w tym samym kolorze co pasek nawigacji, ale baw się alfą, aż uzyskasz pożądane efekty:

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 tyłu

wprowadź opis obrazu tutaj

(Zmieniono kolor z niższych przykładów na podkreślenie przezroczystości. Przezroczystość / rozmycie są bardziej zauważalne podczas ruchu).

Podklasa UINavigationBari umieszczenie tego samego widoku nad tłem, ale za wszystkim innym, prawdopodobnie przyniesie podobne rezultaty, będąc mniej hackerskim.


Inne rozwiązanie, które widziałem, to zabawa z alfą UINavigationBar:

self.navigationController.navigationBar.alpha = 0.5f;

Edycja: Właściwie po przetestowaniu wydaje się, że nie zapewnia to zamierzonego zachowania (ani żadnego zachowania):

.8 alpha

Pasek nawigacyjny z alfą .8

Nieskorygowana alfa

wprowadź opis obrazu tutaj

Oczywiście będziesz chciał to zrobić tylko na urządzeniach z systemem iOS 7. Dlatego przed zaimplementowaniem któregokolwiek z nich dodaj sprawdzanie wersji .

Kevin
źródło
Popieram podejście polegające na aktualizacji alfa i tintColor, aby uzyskać kolor, którego szukasz.
StuartM
Jeśli możesz uzyskać pożądane efekty w ten mniej inwazyjny sposób, to całkowicie. Ale nie jestem pewien, jak skuteczne to będzie.
Kevin
Jedyny problem, jaki otrzymuję z tym (i nie wydaje się, aby był reprezentowany na twoim zdjęciu), polega na tym, że dodaje widok bezpośrednio na pasku nawigacji, w wyniku czego powoduje blokowanie elementów sterujących: cl.ly / image / 3d3C2o0N283S
SpacePyro
Ups, teraz rozumiem. Powinno być[self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];
Kevin
Tak, pomyślałem, że to masz na myśli. ;) To zdecydowanie daje mi lepszą rozpiętość kolorów do wyboru. Uzyskanie intensywności oryginalnego koloru będzie prawdopodobnie niemożliwe, biorąc pod uwagę, że efekt rozmycia nadal naprawdę desaturuje wszystko pod nim. Na razie będę musiał po prostu żyć z ciemniejszym kolorem.
SpacePyro,
4

Zamiast tworzyć obiekt UIColor w formacie RGB , użyj HSB i zwiększ parametr nasycenia. (Podziękowania dla Sama Soffesa, który opisuje tę metodę tutaj )

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

Uwaga: to rozwiązanie jest kompromisem i nie działa dobrze w przypadku kolorów o wysokim nasyceniu.

Aby wybrać kolor HSB z projektu, możesz użyć narzędzia takiego jak ColorSnapper, które pozwala po prostu skopiować format UIColor HSB.

Możesz także wypróbować kategorię UIColor ( GitHub Link ) Davida Keegana, aby zmodyfikować istniejące kolory.

Bernhard
źródło
3

Problem został rozwiązany przez Apple w nowej wersji 7.0.3.

smad
źródło
1
Tak, ich implementacja kolorystyczna zdecydowanie zmieniła się w 7.0.3. Teraz mogę osiągnąć pożądany kolor, podnosząc nasycenie o około 10-15%. Wcześniej musiałem również dodać dodatkową warstwę.
Matej Bukovinski
Dzięki, że dałeś mi znać! W tym celu prześlę aktualizację do mojej biblioteki.
SpacePyro,
1
I nie naprawiono w 7.1> _ <
Leo Natan
1

Użyłem rozwiązania @ aprato, ale znalazłem kilka przypadków narożnych, w których nowe warstwy z nowych VC (np. UINavigationItemButtonViews, Itp. UINavigationItemViews) Byłyby automatycznie wstawiane na pozycję poniżej extraColorLayer(co spowodowałoby, że te tytuły lub elementy przycisków byłyby dotknięte przez, extraColorLayera tym samym jaśniejsze niż normalnie). Tak więc dostosowałem rozwiązanie @ aprato, aby wymusić extraColorLayerpozostanie na pozycji indeksu 1. W pozycji indeksu 1, extraColorLayerpozostaje tuż nad _UINavigationBarBackground, ale pod wszystkim innym.

Oto moja implementacja klasy:

- (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
}
Pan T.
źródło
Ponieważ warstwy i podglądy nie są mieszane, przeładowanie addSubView nie jest konieczne
Rob van der Veer
1

Poprawiłem twój kod w moim fork: https://github.com/allenhsu/CRNavigationController

Dzięki mojej modyfikacji kolor wynikowy na ekranie (wybrany na białym tle) będzie miał dokładnie taką samą wartość przekazaną do setBarTintColor. Myślę, że to niesamowite rozwiązanie.

Allen Hsu
źródło
@JordanBrown nie testowano w 7.1 i 8
Allen Hsu
0

Żaden z tych hacków nie jest wymagany :). Po prostu ustaw:

self.navigationController.navigationBar.translucent = NO;

W systemie iOS 7 domyślna przezroczystość została ustawiona na TRUE.

rishabh
źródło
17
Ale pytanie wymaga również przezroczystości.
Leo Natan
:) Dzięki, u mnie zadziałało. Jednak pod paskiem nawigacyjnym znajduje się czarne dolne obramowanie 1 piksela. Czy jest jakiś sposób, aby to usunąć?
Pritesh Desai
0

A propos, możesz łatwo ustawić kolor tekstu tytułu (z cieniem) poprzez:

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];
bryguy1300
źródło
1
Jeśli próbujesz dopasować styl iOS 7, użycie cienia na tekście tytułu jest prawdopodobnie nieodpowiednie.
Nick Locking
Masz rację, w iOS 7 Apple nie ustawia już cieni w tekście paska nawigacyjnego. Jeśli jednak chcesz mieć niestandardowy wygląd, dałoby to opcję.
bryguy1300
0

Natknąłem się na to pytanie, próbując skonfigurować jednolicie kolorowy pasek nawigacyjny z WYŁĄCZONĄ przezroczystością w systemie iOS 7.

Po pewnym czasie eksperymentowania z barTintColor doszedłem do wniosku, że bardzo łatwym sposobem na posiadanie nieprzezroczystego paska nawigacji jest zrobienie pojedynczego piksela o pożądanym kolorze, zrobienie z niego rozciągliwego obrazu i ustawienie go na tle Obraz paska nawigacji .

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

Trzy linijki kodu, bardzo proste i działające ZARÓWNO na iOS 6, jak i iOS 7 (barTintColor nie jest obsługiwany na iOS 6).

bizz84
źródło
0

Jest świetny zamiennik Dropin UINavigationController dostępny od Simon Booth dostępny na GitHub Tutaj GitHub - C360NavigationBar

Jeśli wstecz obsługujesz iOS6, sprawdź główny kontroler widoku jako taki:

PatientListTableViewController * frontViewController = [[PatientListTableViewControllerocation] 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;
DoctorG
źródło
O ile czegoś nie brakuje, paski tej biblioteki nie są przezroczyste.
Rivera
0

Jak wspomniano powyżej @bernhard , możliwe jest nasycenie koloru barwy paska w celu uzyskania pożądanego wyglądu paska nawigacji.

Napisałem narzędzie BarTintColorOptimizer do tego rodzaju regulacji. Optymalizuje półprzezroczysty odcień paska, aby rzeczywisty kolor paska pasował do żądanego koloru w systemie iOS 7.xi nowszych. Spójrz na tę odpowiedź, aby poznać szczegóły.

IvanRublev
źródło
-1

Szczerze mówiąc, powyższe odpowiedzi mogą być prawidłowe, ale następujący trik zadziałał dla mnie bardzo łatwo.

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

Oto obrazy używane do self.imageRedi self.imageBlack.

< self.imageBlack> czarny obraz w tych nawiasach nie będzie widoczny, ponieważ jest przezroczysty :)

< self.imageRed> czerwony obraz znajduje się w nawiasach.

Sagar R. Kothari
źródło
To nie daje rozmycia.
Ortwin Gentz
-1

czy istnieje sposób na użycie rozwiązania @aprato bez podklasy UINavigationBar.

W moim projekcie moim głównym widokiem jest UIViewController.

problem polega na tym, że navigationController jest właściwością tylko do odczytu, czy istnieje sposób na użycie klasy w moim projekcie, ponieważ nie mogę użyć: [[UINavigationController alloc] initWithNavigationBarClass:

dzięki

amau96
źródło
Nie mógłbyś po prostu owinąć swojego UIViewControllerwnętrza za UINavigationControllerpomocą [[[UINavigationController alloc] initWithRootViewController:<YourViewController>]? Można też lepiej odpowiedzieć na to pytanie jako osobne pytanie, zamiast pisać tutaj.
SpacePyro,
-2

Prostym sposobem na uzyskanie pożądanego koloru jest użycie

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

Tak długo, jak obraz ma trochę alfa, przezroczystość będzie działać i możesz ustawić alfa, zmieniając obraz. To właśnie zostało dodane w iOS7. Szerokość i wysokość obrazu to 640x88px w pionie (dodaj 20 do 88, jeśli chcesz, aby znajdował się pod paskiem stanu).

Furman
źródło
To jednak nie zachowuje rozmycia. Jest przejrzystość, ale nie ma rozmycia.
Leo Natan