Hintergrund einer UINavigationBar anpassen

Von am 26.04.2011 | 2 Comments

Problem: Viele iOS Apps haben eine angepasste und oft aufwändig entiwckelte GUI. So müssen auch standard Elemente, wie die UINavigationBar, grafisch angepasst werden. iBooks und Notizen sind seitens Apple die bekanntesten Beispiele für eine angepasste NavigationBar. Wie eine solche Anpassung realisiert wird erkläre ich euch anhand dem kommenden Beispiel.

Vorbereitung: Um mit unserem Beispiel starten zu können benötigen wir ein lauffähiges App. Dazu legt Ihr einfach ein neues Projekt auf Basis des “Navigation-Based” Templates an und fügt einen “DetailView” hinzu.
Zu diesem Zeitpunkt sollte eure App funktionsfähig sein und so ähnlich aussehen wie die folgenden Bilder.

Lösung: Der nächste Schritt zur eigenen UINavigationBar ist die Erstellung einer eigenen Subclass der UINavigationBar. Dies machen wir, indem wir eine neue Datei anlegen und als Template wählen wir “Objective-C class”.
Aus dem Drop-Down für die “Subclass” selektieren wir NSObject. Als Dateiname geben wir “CustomUINavigationBar” an.
Da es hier keine Vorlage für eine UINavigationBar gibt, werden wir die Umstellung manuell vornehmen, das geht sehr einfach.
Wir müssen lediglich in den neu erstellten Dateien
“NSObject” durch “UINavigationBar” ersetzen.

// Alt
@interface CustomUINavigationBar : NSObject {...}
// Neu
@interface CustomUINavigationBar : UINavigationBar {...}

Ist die neue Subclass gesetzt, müssen wir noch eine neue Property definieren. Diese Property benötigen wir, um sie gleich im Interface Builder verknüpfen zu können. Den Code, den wir nun benötigen, sieht wie folgt aus:

// CustomUINavigationBar.h
@interface CustomUINavigationBar : UINavigationBar{
IBOutlet UINavigationController* navigationController;
}

@property (nonatomic, retain) IBOutlet UINavigationController* navigationController;

@end
// CustomUINavigationBar.m
#import "CustomUINavigationBar.h"

@implementation CustomUINavigationBar
@synthesize navigationController;

- (void)dealloc{
[navigationController release];
[super dealloc];
}

@end

Sobald die neue Subclass und das Outlet (Property) definiert ist, öffnen wir das “MainWindow.xib” im InterfaceBuilder.
Dort klappen wir den UINavigationController auf (um dies machen zu können solltet Ihr euch im Listen Modus befinden). Innerhalb des “UINavigationControllers” findet Ihr eine “UINavigationBar”, genau diese wird von uns benötigt.
Mit einem Klick wählen wir die NavigationBar aus und öffnen den “Identity-Inspector” (Shortcut: ⌘+4). Ist dieser geöffnet, finden wir dort ein Feld “Class-Identity”, hier geben wir nun unsere “CustomUINavigationBar” an.

Nach dieser Zuweisung fehlt uns nur noch eine Sache im Interface Builder. Dazu wechseln wir in den Tab “Connections” (Shortcut: ⌘+2), hier können wir das Outlet des NavigationController  zuweisen. Dies machen wir per Drag&Drop (siehe nächstes Bild).

An dieser Stelle sind die Arbeiten im Interface Builder abgeschlossen.
Wir sollten nun alle Dateien speichern, Interface Builder beenden und zurück zu Xcode wechseln. Da unsere Subclass aktuell keine Funktionen hat bzw. überschrieben ist, ist  unsere App auch an dieser Stelle lauffähig (jedoch sehen wir noch keine Veränderungen in der NavigationBar).

Zurück in XCode öffnen wir die Datei “CustomUINavigationBar.h”.
Hier müssen wir nun eine weitere Property und zwei neue Funktionen definieren.
Zum einen definieren wir eine Property für unser neues Hintergrundbild (UIImageView *backgroundImageView) und zwei Funktionen zum Hinzufügen und entfernen des Hintergrundbildes.
Unser Code in “CustomUINavigationBar.h” sieht nun wie folgt aus.

@interface CustomUINavigationBar : UINavigationBar {
UIImageView *backgroundImageView;
IBOutlet UINavigationController *navigationController;
}

@property (nonatomic, retain) UIImageView *backgroundImageView;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

-(void) setBackgroundImage:(UIImage*)backgroundImage;
-(void) clearBackgroundImageView;

@end

Die “CustomUINavigationBar.m” sieht vorerst so aus:

#import "CustomUINavigationBar.h"

@implementation CustomUINavigationBar
@synthesize backgroundImageView, navigationController;

-(void) setBackgroundImage:(UIImage*)backgroundImage {
}

-(void) clearBackgroundImageView {
}

- (void) dealloc {
[backgroundImageView release];
[navigationController release];
[super dealloc];
}

@end

Zusätzlich zu diesen Funktionen benötigen wir noch die Funktion “drawRect:(CGRect)rect”. Diese Funktion zeichnet unser “backgroundImageView” als Hintergrundbild unserer NavigationBar, dies soll aber nur geschehen, wenn wir auch wirklich ein Hintergrundbild haben.
Die Implementierung der Funktion sieht wie folgt aus:

- (void) drawRect:(CGRect)rect {
if (backgroundImageView) {
[backgroundImageView.image drawInRect:rect];
} else {
[super drawRect:rect];
}
}

Kommen wir nun zu der Funktion die uns das Hintergrundbild setzt. In diese Funktion übergeben wir ein UIImage. In der ersten Zeile der Funktion erstellen wir eine neue Instanz von UIImageView. In der nächsten Zeile setzten wir das übergebene Bild als Image unseres UIImageView`s. In der letzten Zeile rufen wir “setNeedsDisplay” auf, diese Funktion erzwingt das die NavigationBar neu gezeichnet wird und ruft die von uns überschrieben “drawRect” auf.
Somit wird unser Hintergrundbild erstellt und “eingefügt”.

-(void) setBackgroundImage:(UIImage*)backgroundImage {
self.backgroundImageView = [[[UIImageView alloc] initWithFrame:self.frame] autorelease];
backgroundImageView.image = backgroundImage;
[self setNeedsDisplay];
}

Die Funktion “clearBackgroundImageView” ist recht einfach, denn dort setzten wir unsere “backgroundImageView” Property lediglich auf “nil” und erzwingen durch den Aufruf von “setNeedsDisplay” das unsere NavigationBar neu gezeichnet wird. Da unser Hintergrundbild zu diesem Zeitpunkt nicht vorhanden ist, wird in “drawRect” die “originale / super drawRect” der NavBar aufgerufen.

-(void) clearBackgroundImageView {
self.backgroundImageView = nil;
[self setNeedsDisplay];
}

Nun kommen wir zur Implementierung unserer Funktionen. Dazu wechselt Ihr in euren “RootViewController.m”, importiert die “CustomUINavigationBar.h” und geht in die Funktion “viewDidLoad”, dort ergänzen wir nun folgende zwei Zeilen:

CustomUINavigationBar* customUINavigationBar = (CustomUINavigationBar*)self.navigationController.navigationBar;
[customUINavigationBar setBackgroundImage:[UIImage imageNamed:@"NavigationBarBackground.png"]];

In Zeile 1 holen wir die NavigationBar unseres NavControllers. Da dieser vom Typ “CustomUINavigationController” ist wenden wir auch ein Typecast an. Zeile 2 beschäftigt sich nun mit dem Aufruf unserer “setBackgroundImage” Funktion. Das war auch schon der ganze Zauber, im Ergebnis sieht das ganz dann so aus:

Damit die zwei Buttons etwas besser in den neuen Hintergrund passen, könnt Ihr einfach die “tintColor” der NavigationBar anpassen. Dies verändert nicht nur die Farbe der NavigationBar, sondern auch die Farbe eurer Buttons…

customUINavigationBar.tintColor = [UIColor greyColor];

  • Robin

    Kann man auch nur Buttons ändern?

  • Jannik Egger

    Buttons habe ich noch nicht getestet.
    Aber wenn du die Buttons ebenfalls subcalsst und anschließend manuell in die UINavigationBar setzt, funktioniert das bestimmt.
    Ich schaue mal ob ich die Tage Zeit finde, ein Beispiel mit den Buttons zu erstellen!

    Beste Grüße,
    Jannik