Sfondo personalizzato UINavigationBar

Ho cercato di impostare uno sfondo personalizzato per l’intera mia barra di navigazione (non solo per titleView) ma ho avuto difficoltà.

Ho trovato questo thread

http://discussions.apple.com/thread.jspa?threadID=1649012&tstart=0

Ma non sono sicuro di come implementare lo snippet di codice che viene fornito. Il codice è implementato come una nuova class? Inoltre, dove installo UINavigationController perché ho un’applicazione creata con il modello NavigationView in modo che non venga eseguita nel mio controller di root come nell’esempio

Devi solo sovraccaricare drawRect in questo modo:

 @implementation UINavigationBar (CustomImage) - (void)drawRect:(CGRect)rect { UIImage *image = [UIImage imageNamed: @"NavigationBar.png"]; [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; } @end 

Uddhav e leflaw hanno ragione. Questo codice funziona bene:

 @interface CustomNavigationBar : UINavigationBar @end @implementation CustomNavigationBar -(void) drawRect:(CGRect)rect { UIImage *image = [UIImage imageNamed: @"myNavBarImage"]; [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; } @end // this can go anywhere +(UINavigationController*) myCustomNavigationController { MyViewController *vc = [[[MyViewController alloc] init] autorelease]; UINavigationController *nav = [[[NSBundle mainBundle] loadNibNamed:@"CustomNavigationController" owner:self options:nil] objectAtIndex:0]; nav.viewControllers = [NSArray arrayWithObject:vc]; return nav; } 

Devi creare CustomNavigationController.xib e inserire un UINavigationController e modificare la class navigationBar in “CustomNavigationBar”.

È necessario utilizzare il proxy ‘aspetto’ per modificare lo sfondo e altre proprietà di stile di controlli come UINavigationBar , UIToolBar ecc. In iOS 5.xx. Tuttavia, questi non sono disponibili per iOS 4.xx quindi per compatibilità con le versioni precedenti, è necessaria una soluzione ibrida.

Se si desidera supportare entrambi i dispositivi iOS 4.xx e iOS 5.xx (ovvero il proprio DeploymentTarget è 4.xx), è necessario prestare attenzione nel trasferire la chiamata al proxy dell’aspetto controllando in fase di esecuzione se è presente il selettore ‘aspetto’ o no.

Puoi farlo da:

 //Customize the look of the UINavBar for iOS5 devices if ([[UINavigationBar class]respondsToSelector:@selector(appearance)]) { [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"NavigationBar.png"] forBarMetrics:UIBarMetricsDefault]; } 

Dovresti anche abbandonare la soluzione alternativa di iOS 4.xx che potresti aver implementato. Se hai implementato la soluzione alternativa drawRect per i dispositivi iOS 4.xx, come menzionato da @ludwigschubert, dovresti lasciarlo in:

 @implementation UINavigationBar (BackgroundImage) //This overridden implementation will patch up the NavBar with a custom Image instead of the title - (void)drawRect:(CGRect)rect { UIImage *image = [UIImage imageNamed: @"NavigationBar.png"]; [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; } @end 

In questo modo, la NavBar avrà lo NavBar aspetto sia nei dispositivi iOS 4 che iOS 5.

L’implementazione di una categoria non è consigliabile. iOS5 può fornire sollievo per questo problema. Ma per le vecchie API, puoi –

  1. Sottoclass UINavigationBar per dire CustomNavBar e implementa il drawRect personalizzato dalla risposta di Lithium.
  2. Per tutti i UINavigationControllers basati su IB, fornire CustomNavBar come class personalizzata per UINavigationBar .
  3. Per tutti i codici basati su UINavigationControllers . Creare un XIB con un UINavigationController e fare il passo due. Quindi fornire un metodo factory in codice che carichi UINavigationController dal pennino e fornire un IBOutlet .

Per esempio.

 [[NSBundle mainBundle] loadNibNamed:@"CustomNavigationController" owner:self options:nil]; UINavigationController *navController = self.customNavigationController; navController.viewControllers = [NSArray arrayWithObject:controller] 

Puoi anche eseguire l’override del drawLayer:inContext: metodo in una class di class UINavigationBar . All’interno del drawLayer:inContext: metodo, è ansible disegnare l’immagine di sfondo che si desidera utilizzare.

 - (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)context { if ([self isMemberOfClass:[UINavigationBar class]] == NO) { return; } UIImage *image = (self.frame.size.width > 320) ? [UINavigationBar bgImageLandscape] : [UINavigationBar bgImagePortrait]; CGContextClip(context); CGContextTranslateCTM(context, 0, image.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage); } 

E come un progetto completo di Xcode dimostrativo sulla personalizzazione dell’aspetto di UINavigationBar questo e questo potrebbero essere utili.

L’implementazione di una categoria non funzionerà in iOS5, dovresti utilizzare il consiglio di Uddhav Kambli per l’utilizzo di CustomNavbar su iOS ≤ 5.

Ho appena trovato questo post di blog, descrivendo questo argomento in modo molto semplice: http://web0.at/blog/?p=38

mi ha aiutato molto, usano il metodo “drawRect” per ottenere la personalizzazione dello sfondo.

Per tutti coloro che hanno problemi con gli sfondi personalizzati di UINavigationBar in iOS5, farlo nei corrispondenti metodi viewDidLoad:

 #if defined(__IPHONE_5_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0 if ([self.navigationController.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){ [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"TitleBar"] forBarMetrics:UIBarMetricsDefault]; } #endif 

Si noti che nel mio caso l’immagine di sfondo è stata nominata “TitleBar”. Puoi inserire qualunque sia il tuo nome di immagine di sfondo personalizzato.

Il problema è che se si utilizza un controller di navigazione, il titolo di ogni pagina si sovrapporrà alla barra di navigazione personalizzata. Se la tua barra di navigazione contiene un logo o il nome della tua app, questo è ovviamente inaccettabile.

È ansible impostare il titolo di ciascuna vista nella pila di navigazione in bianco, ma alcune viste impongono un titolo a cui non si può fare nulla (come il photo picker). Quindi potresti voler creare un’immagine di barra di navigazione alternativa con lo stesso colore o design della barra di navigazione del logo, ma con un’area vuota per fare spazio a titoli sovrapposti.

Per cambiare a piacere le immagini della barra di navigazione, aggiungi una proprietà alla tua delegata dell’app per contenere il nome dell’immagine della barra di navigazione e sostituisci la prima riga del primo esempio sopra con questi due:

  YourAppDelegate* theApp = (YourAppDelegate*)[[UIApplication sharedApplication] delegate]; UIImage* image = [UIImage imageNamed:theApp.navBarName]; 

Quindi, nel primo controller della vista che inserirai nello stack di navigazione, procedi in questo modo:

 - (void)viewWillAppear:(BOOL)animated { YourAppDelegate* theApp = (YourAppDelegate*)[[UIApplication sharedApplication] delegate]; theApp.navBarName = @"navBar_plain"; } 

Quindi, nel controller della vista radice, fare la stessa cosa ma specificare l’immagine della barra di navigazione che porta il logo, in modo che venga ripristinata quando l’utente torna indietro e non c’è titolo in conflitto.

Un altro approccio è usare il delegato di UINavigationController. Non richiede la sottoclass / sovrascrittura della class UINavigationBar:

 /* in the place where you init the navigationController: fixer = [[AENavigationControllerDelegate alloc] init]; navigationController.delegate = fixer; */ @interface AENavigationControllerDelegate : NSObject  @end @implementation AENavigationControllerDelegate #define bgImageTag 143 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { //this is for the future for implementing with the appearance api: if ([[navigationController navigationBar] respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [[navigationController navigationBar] setBackgroundImage:[UIImage imageNamed:@"header-logo-bg.png"] forBarMetrics:UIBarMetricsDefault]; }); } else { UIImageView* imageView = (UIImageView*)[navigationController.navigationBar viewWithTag:bgImageTag]; if(!imageView) { UIImage *image = [UIImage imageNamed:@"header-logo-bg.png"]; imageView = [[[UIImageView alloc] initWithImage:image] autorelease]; imageView.tag = bgImageTag; } [navigationController.navigationBar insertSubview:imageView atIndex:0]; } } @end 

https://gist.github.com/1184147

In iOS5, il valore zPosition (del livello più profondo di UINavigationBar's ) viene modificato. Quindi se cambi quella zPosition , la vecchia maniera funziona.

per esempio.

 UINavigationBar *_bar = navigationController.navigationBar; // Insert ImageView UIImage *_img = [UIImage imageNamed:@"navibar.png"]; UIImageView *_imgv = [[[UIImageView alloc] initWithImage:_img] autorelease]; _imgv.frame = _bar.bounds; UIView *v = [[_bar subviews] objectAtIndex:0]; v.layer.zPosition = -FLT_MAX; _imgv.layer.zPosition = -FLT_MAX+1; [_bar insertSubview:_imgv atIndex:1]; 

Questo script gestisce il livello di vista, quindi devi importare QuartzCore .

Ecco una soluzione alternativa che ti consente di utilizzare la sottoclass personalizzata di UINavigationBar:

https://gist.github.com/1253807

Come ha affermato la stessa Apple, non è corretto sovrascrivere i metodi nelle Categorie. Quindi il modo migliore per personalizzare lo sfondo di UINavigarionBar è la sottoclass e l’override -(void)drawInRect: method.

 @implementation AppNavigationBar - (void)drawRect:(CGRect)rect { UIImage *patternImage = [UIImage imageNamed:@"image_name.png"]; [patternImage drawInRect:rect]; } 

Per utilizzare questa UINavigationBar personalizzata, questa deve essere impostata come proprietà navigationBar del tuo UINavigationBarController . Come sai questa proprietà è in sola lettura. Quindi, ciò che dovrebbe essere fatto è:

 - (void)viewDidLoad { [super viewDidLoad]; AppNavigationBar *nav = [AppNavigationBar new]; [self setValue:nav forKey:@"navigationBar"]; } 

Funziona sia per iOS 5 e 4.3.

Puoi sottoclass UINavigationBar e abilitarlo in questo modo, poiché le categorie per drawRect non funzioneranno più in iOS5

  navigationController = [[((^ { NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:[NSKeyedArchiver archivedDataWithRootObject:navigationController]]; [unarchiver setClass:[SAPHUINavigationBar class] forClassName:@"UINavigationBar"]; [unarchiver setClass:[UIViewController class] forClassName:NSStringFromClass([navigationController.topViewController class])]; return unarchiver; })()) decodeObjectForKey:@"root"] initWithRootViewController:navigationController.topViewController]; 

Per una vista statica (nessuna animazione), utilizzo il setBackgroundImage predefinito di iOS

Ma quando ho una vista animata (ridimensiona molto probabilmente), creo un UIImageView personalizzato e lo aggiungo alla barra di navigazione in modo da avere più flessibilità su di esso

Il fatto è che se lo aggiungi, verrà visualizzato in cima ai pulsanti e al titleView, quindi salverò manualmente una copia della maggior parte delle sottoview, rimuoverle dalla vista principale, aggiungere la mia imageView e aggiungere tutte le sottoview indietro

Questo funziona per me

  UIImageView *navBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"navigationBackgroundSample.jpg"]]; UIView *tempTitleView = [[self.navigationBar.subviews objectAtIndex:1] autorelease]; [[self.navigationBar.subviews objectAtIndex:1] removeFromSuperview]; [self.navigationBar addSubview:navBackground]; [self.navigationBar addSubview:tempTitleView]; self.navigationBar.clipsToBounds = YES; [navBackground release]; 

In questo caso, non ho i pulsanti e ho scoperto che il mio titleView è all’indice 1, se hai i pulsanti, dovrebbero trovarsi da qualche parte nell’array dei sottoview di navigationBar

Non so cosa c’è all’indice 0, non so se questo può funzionare nel caso in cui tu non abbia il titolo del testo …