Obtenir des couleurs vives et vives pour une UINavigationBar translucide iOS 7

172

MISE À JOUR iOS 7.1 : Il semble que la solution de contournement pour modifier le canal alpha dans UINavigationBar a été ignorée dans cette mise à jour. À l'heure actuelle, la meilleure solution semble être de simplement `` y faire face '' et d'espérer que la couleur que vous choisissez peut rendre un effet translucide. Je cherche toujours des moyens de contourner ce problème.


MISE À JOUR iOS 7.0.3 : La bibliothèque GitHub que nous avons créée a été mise à jour pour contourner légèrement ce problème lors de l'utilisation d'iOS 7.0.3. Malheureusement, il n'existe pas de formule magique pour prendre en charge les deux couleurs créées dans iOS 7.0.2 et versions antérieures et iOS 7.0.3. On dirait qu'Apple a amélioré la saturation, mais au prix de l'opacité (puisque la translucidité floue dépend du niveau d'opacité). Je travaille, avec quelques autres, à créer une bien meilleure solution à ce problème.


Je suis sûr que beaucoup de gens ont déjà rencontré le problème où iOS 7 a tendance à désaturer la couleur d'un UINavigationBar qui est translucide.

Mon objectif est de réaliser un UINavigationBar avec cette teinte de couleur, mais translucide:

UINavigationBar, Opaque

Cependant, avec la translucidité, je comprends cela. La vue d'arrière-plan est blanche, ce qui, je comprends, rendra cette vue un peu plus claire:

UINavigationBar, translucide

Existe-t-il un moyen d'obtenir la couleur d'origine tout en conservant la translucidité? J'ai remarqué que Facebook avait réussi à faire de sa barre sa couleur bleue riche, comme indiqué ici:

Facebook UINavigationBar, translucide

.. donc je sais qu'il doit y avoir un moyen. Les vues d'arrière-plan font évidemment une différence ici, mais la plupart de leur contenu est également gris / blanc. Il semble que quelle que soit la couleur de teinte de barre que vous mettez, vous ne pouvez pas obtenir des couleurs vives sous translucidité.

Mis à jour avec la solution.

Voici la solution que j'ai finalement trouvée. J'ai pris la solution d ' aprato et j'ai ensuite inclus la coutume UINavigationBardans une UINavigationControllersous - classe. J'ai créé un référentiel dont cette implémentation est répertoriée ci-dessous, avec un exemple d'application .

////////////////////////////
// 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
la source
Facebook iOS7 n'est-il pas UINAvigationBaropaque?
Vinzzz le
Non, c'est une transparence beaucoup plus subtile que l'iOS par défaut. Beaucoup mieux, IMO.
Kevin le
Barre de navigation Facebook non transparente
LE SANG
7
Il est définitivement translucide; s'il vous plaît voir ma réponse modifiée.
SpacePyro
2
@Odelya - Ce n'est pas une solution pour obtenir les bonnes couleurs, mais plutôt une solution pour corriger la légèreté du UINavigationBarmieux possible lorsqu'elle est exposée à la translucidité sous iOS 7.
SpacePyro

Réponses:

52

MISE À JOUR iOS 7.0.3: Comme vous le voyez ci-dessus, la version 7.0.3 a changé les choses. J'ai mis à jour mon essentiel. Espérons que cela disparaîtra à mesure que les gens passeront à niveau.

Réponse originale: Je me suis retrouvé avec un hack combinant les deux autres réponses. Je sous-classe UINavigationBar et j'ajoute une couche à l'arrière avec un espace supplémentaire pour couvrir si l'une des différentes barres d'état de hauteur est en place. Le calque est ajusté dans les sous-vues de mise en page et la couleur change chaque fois que vous définissez 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;

layoutSous-vues

  [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
la source
Où avez-vous implémenté cela sur le ViewController? Sous viewDidLoad?
Jeremy
Cela fonctionne à merveille! J'ai décidé d'ajouter ceci à ma UINavigationControllersous - classe. Voici un aperçu de celui-ci pour ceux qui souhaitent avoir une coutume UINavigationBardans un fichier UINavigationController.
SpacePyro
@Jeremy comme SpacePyro J'ai une sous-classe de contrôleur de navigation (qui, comme la barre que j'utilisais avant 7 pour le ciblage UIAppearance), remplace initWithRootViewControllerpour l'utiliser. J'ai mis à jour mon essentiel
Anthony
Euh… d'accord… Je pense que j'ai besoin de voir ça dans un projet de test entier pour voir pleinement ce que tu veux dire. J'ai pu reproduire l'exemple de timeuser ci-dessous mais je suis toujours un peu vert avec celui-ci.
Jeremy
1
@ Mr.T Merci! Quand j'ai écrit, ce n'était pas encore poussant :) Mais je l'ai mis à jour maintenant, j'ai ajouté UIAappearance pour l'opacité et le support de NSCoding
Anthony
10

Le comportement de tintColor pour les barres a changé sur iOS 7.0. Il n'affecte plus l'arrière-plan de la barre et se comporte comme décrit pour la propriété tintColor ajoutée à UIView. Pour teinter l'arrière-plan de la barre, veuillez utiliser -barTintColor.Vous pouvez utiliser le code suivant pour que l'application fonctionne avec ios6 et ios7.

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

IS_IOS7 est une macro qui est définie dans le fichier pch comme suit.

#define IS_IOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
Bhavesh
la source
9

Je n'ai pas trouvé cette solution mais elle semble fonctionner assez bien. Je viens de l'ajouter à viewDidLoad sur ma sous-classe de UINavigationController.

Source: 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
la source
1
Il semble qu'après avoir poussé un nouveau contrôleur de vue sur le contrôleur de navigation, tous les éléments de bouton de barre sont déplacés pour être sous cette couche de transparence.
Evan Davis
Les titres sont également masqués par cette couche de transparence.
Nick Locking
J'ai écrit cet essentiel. Les commentaires sont justes; soit les éléments de bouton sont poussés en dessous lorsqu'un nouveau contrôleur de vue est poussé, soit les sous-couches héritent de l'alpha de colourView. J'ai bifurqué l'autre réponse et j'ai corrigé quelques éléments dans une nouvelle classe à accès libre
Alan Zeino
J'ai pu mettre le texte en blanc en sélectionnant le style Translucent Black Navigation Bar ... cela a fait l'affaire pour moi. Définissez également yourBar.tintColor = [UIColor whiteColor]; pour d'autres boutons personnalisés sur le dessus.
Juan Zamora
6

Un moyen low-fi serait probablement d'épingler une UIViewhauteur de la barre de navigation en haut de la vue derrière la barre. Faites en sorte que cette vue ait la même couleur que la barre de navigation mais jouez avec l'alpha jusqu'à obtenir les effets souhaités:

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 derrière

entrez la description de l'image ici

(Changement de la couleur des exemples inférieurs à l'accentuation de la transparence. La transparence / le flou est plus visible en mouvement.)

Sous- UINavigationBarclasser et placer cette même vue au-dessus de l'arrière-plan, mais derrière tout le reste, obtiendra probablement des résultats similaires tout en étant moins piraté.


Une autre solution que j'ai vue est de jouer avec l'alpha du UINavigationBar:

self.navigationController.navigationBar.alpha = 0.5f;

Edit: En fait, après les tests, il semble que cela ne donne pas le comportement prévu (ni aucun comportement):

.8 alpha

Barre de navigation avec .8 alpha

Alpha non ajusté

entrez la description de l'image ici

Évidemment, vous ne voudrez le faire que sur les appareils iOS 7. Alors, ajoutez une vérification de version avant d'implémenter l'un de ces éléments.

Kevin
la source
Je seconde l'approche de la mise à jour de l'alpha et du tintColor pour obtenir la couleur que vous recherchez.
StuartM
Si vous pouvez obtenir les effets souhaités de cette manière moins invasive, alors totalement. Mais je ne suis pas sûr de son efficacité.
Kevin le
Le seul problème que j'obtiens avec cela (et cela ne semble pas représenté sur votre image) est qu'il ajoute la vue directement en haut de la barre de navigation, et par conséquent, cela provoque le blocage des contrôles: cl.ly / image / 3d3C2o0N283S
SpacePyro
Oups, maintenant je vois. Devrait être[self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];
Kevin
Ouais, j'ai pensé que c'était ce que tu voulais dire. ;) Cela me donne définitivement une meilleure gamme de couleurs à choisir. Obtenir l'éclat de la couleur d'origine sera probablement impossible étant donné que l'effet de flou désature toujours tout ce qui se trouve en dessous. Je vais juste devoir vivre avec une couleur plus foncée pour le moment.
SpacePyro
4

Au lieu de créer votre objet UIColor au format RVB , utilisez HSB et augmentez le paramètre de saturation. (Crédits à Sam Soffes qui décrit cette méthode ici )

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

Remarque: cette solution est un compromis et ne fonctionne pas bien pour les couleurs à forte saturation.

Pour choisir la couleur HSB de votre conception, vous pouvez utiliser un outil comme ColorSnapper qui vous permet de copier simplement le format UIColor HSB.

Vous pouvez également essayer la catégorie UIColor ( lien GitHub ) de David Keegan pour modifier les couleurs existantes.

Bernhard
la source
3

Le problème a maintenant été résolu par Apple dans la nouvelle version 7.0.3.

smad
la source
1
Oui, leur implémentation de couleur de teinte a définitivement changé dans la version 7.0.3. Je peux maintenant obtenir la couleur souhaitée en augmentant simplement la saturation d'environ 10 à 15%. Avant, j'avais également besoin d'ajouter la couche supplémentaire.
Matej Bukovinski
Merci de me le faire savoir! Je vais pousser une mise à jour dans ma bibliothèque pour cela.
SpacePyro
1
Et non corrigé dans 7.1> _ <
Leo Natan
1

Je @ solution de aprato mais trouvé quelques cas de coin où les nouvelles couches de nouveaux VCs (par exemple. UINavigationItemButtonViews, UINavigationItemViews, Etc.) seraient insérés automatiquement dans une position inférieure à la extraColorLayer(qui provoquerait ces éléments de titre ou bouton d'être affectés par la extraColorLayeret donc de couleur plus pâle qu’ils ne le seraient normalement). J'ai donc ajusté la solution de @ aprato pour forcer le extraColorLayerà rester à la position d'index 1. À la position d'index 1, le extraColorLayerreste juste au-dessus de _UINavigationBarBackground, mais en dessous de tout le reste.

Voici ma mise en œuvre de classe:

- (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
}
M. T
la source
Étant donné que les couches et les sous-vues ne sont pas mélangées, il n'est pas nécessaire de surcharger addSubView
Rob van der Veer
1

J'ai amélioré votre code dans mon fork: https://github.com/allenhsu/CRNavigationController

Avec ma modification, la couleur du résultat à l'écran (choisie sur fond blanc) sera exactement la même valeur que celle transmise setBarTintColor. Je pense que c'est une solution incroyable.

Allen Hsu
la source
@JordanBrown non testé sur 7.1 et 8
Allen Hsu
0

Aucun de ces hacks n'est requis :). Réglez simplement:

self.navigationController.navigationBar.translucent = NO;

Pour iOS 7, la translucidité par défaut a été conservée sur TRUE.

rishabh
la source
17
Mais la question nécessite également de la translucidité.
Leo Natan
:) Merci, cela a fonctionné pour moi. Cependant, il y a une bordure inférieure noire de 1px sous la barre de navigation. Existe-t-il un moyen de le supprimer?
Pritesh Desai
0

Sur une note connexe, vous pouvez définir facilement la couleur du texte de votre titre (avec une ombre) via:

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
la source
1
Si vous essayez de faire correspondre le style d'iOS 7, l'utilisation d'une ombre portée sur le texte de votre titre est probablement inappropriée.
Nick Locking le
Vous avez raison, dans iOS 7, Apple ne définit plus d'ombres sur le texte de sa barre de navigation. Si vous vouliez un look personnalisé, cela vous donnerait l'option.
bryguy1300
0

Je suis tombé sur ce Q / R en essayant de configurer une barre de navigation de couleur uniforme avec la transparence DÉSACTIVÉE sur iOS 7.

Après avoir expérimenté un certain temps avec barTintColor, j'ai compris qu'un moyen très simple d'avoir une barre de navigation opaque consiste à créer une image à un seul pixel de la couleur désirée, à en faire une image extensible et à la mettre en arrière-plan Image de la barre de navigation .

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

Trois lignes de code, très simples et fonctionnent à LA FOIS sur iOS 6 et iOS 7 (barTintColor n'est pas pris en charge sur iOS 6).

bizz84
la source
0

Il existe un excellent remplacement de Dropin UINavigationController disponible auprès de Simon Booth disponible sur GitHub ici GitHub - C360NavigationBar

Si vous prenez en charge iOS6 en arrière, vérifiez le contrôleur de vue racine en tant que tel:

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;
DocteurG
la source
Sauf si je manque quelque chose, les barres de cette bibliothèque ne sont pas translucides.
Rivera
0

Comme @bernhard l'a mentionné ci-dessus, il est possible de saturer la couleur de la teinte de la barre pour obtenir l'apparence souhaitée de la barre de navigation.

J'ai écrit un utilitaire BarTintColorOptimizer pour ce type d'ajustement. Il optimise la couleur de teinte translucide de la barre pour que la couleur réelle de la barre corresponde à la couleur souhaitée dans iOS 7.x et versions ultérieures. Regardez cette réponse pour plus de détails.

IvanRublev
la source
-1

Franchement, les réponses ci-dessus pourraient être exactes, mais le truc suivant a fonctionné pour moi très facilement.

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

Voici les images utilisées pour self.imageRedetself.imageBlack .

< self.imageBlack > l'image noire est entre ces crochets ne sera pas visible car elle est transparente :)

< self.imageRed> l'image rouge est entre ces crochets.

Sagar R. Kothari
la source
Cela ne vous donne pas de flou.
Ortwin Gentz
-1

existe-t-il un moyen d'utiliser la solution @aprato sans sous-classer UINavigationBar.

Dans mon projet, ma vue principale est un UIViewController.

le problème est que navigationController est une propriété en lecture seule, existe-t-il un moyen d'utiliser votre classe avec mon projet car je ne peux pas utiliser: [[UINavigationController alloc] initWithNavigationBarClass:

Merci

amau96
la source
Tu ne pourrais pas juste envelopper ton UIViewControllerintérieur UINavigationControlleravec [[[UINavigationController alloc] initWithRootViewController:<YourViewController>]? Il peut également être préférable de répondre à cette question sous forme de question distincte, plutôt que de publier ici.
SpacePyro
-2

Un moyen simple d'obtenir la couleur souhaitée consiste à utiliser

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

Tant que votre image a un alpha, la translucidité fonctionnera et vous pouvez définir l'alpha en changeant l'image. Cela vient d'être ajouté dans iOS7. La largeur et la hauteur de l'image sont de 640x88px pour la verticale (ajoutez 20 au 88 si vous voulez qu'il soit sous la barre d'état).

Charretier
la source
Cela ne conserve cependant pas de flou. Il y a de la transparence, mais pas de flou.
Leo Natan