Por lo que puedo decir, a veces es necesario subclasificar UINavigationBar, para hacer un rediseño no estándar. A veces es posible evitar tener que hacerlo mediante el uso de categorías , pero no siempre.
Actualmente, hasta donde yo sé, la única forma de configurar un UINavigationBar personalizado dentro de un UIViewController es a través de IB (es decir, a través de un archivo); probablemente no debería ser así, pero por ahora, debemos vivir con él.
Esto suele estar bien, pero a veces utilizar IB no es realmente factible.
Entonces, vi tres opciones:
- Subclase UINavigationBar y conéctelo todo en IB, luego ensucie la carga de la punta cada vez que quiera un UINavigationController,
- Use el reemplazo de método dentro de una categoría para cambiar el comportamiento de UINavigationBar, en lugar de subclasificar, o
- Subclase UINavigationBar y haga un poco de trabajo con archivar / desarchivar el UINavigationController.
La opción 1 fue inviable (o al menos demasiado molesta) para mí en este caso, ya que necesitaba crear el UINavigationController programáticamente, 2 es un poco peligrosa y más una opción de último recurso en mi opinión, así que elegí la opción 3.
Mi enfoque fue crear un archivo de 'plantilla' de un UINavigationController, y desarchivarlo, devolviéndolo en formato initWithRootViewController
.
Así es cómo:
En IB, creé un UINavigationController con el conjunto de clases apropiado para UINavigationBar.
Luego, tomé el controlador existente y guardé una copia archivada usando +[NSKeyedArchiver archiveRootObject:toFile:]
. Acabo de hacer esto dentro del delegado de la aplicación, en el simulador.
Luego utilicé la utilidad 'xxd' con el indicador -i, para generar código c desde el archivo guardado, para incrustar la versión archivada en mi subclase ( xxd -i path/to/file
).
Dentro initWithRootViewController
desarchivo esa plantilla, y me pongo al resultado del desarchivar:
// This is the data from [NSKeyedArchiver archivedDataWithRootObject:controller], where
// controller is a CTNavigationController with navigation bar class set to CTNavigationBar,
// from IB. This c code was created using 'xxd -i'
static unsigned char archived_controller[] = {
0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03,
...
};
static unsigned int archived_controller_len = 682;
...
- (id)initWithRootViewController:(UIViewController *)rootViewController {
// Replace with unarchived view controller, necessary for the custom navigation bar
[self release];
self = (CTNavigationController*)[NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithBytes:archived_controller length:archived_controller_len]];
[self setViewControllers:[NSArray arrayWithObject:rootViewController]];
return [self retain];
}
Luego, puedo simplemente tomar una nueva instancia de mi subclase UIViewController que tiene configurada la barra de navegación personalizada:
UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease];
[self.navigationController presentModalViewController:modalViewController animated:YES];
Esto me da un UITableViewController modal con una barra de navegación y una barra de herramientas configuradas, y con la clase de barra de navegación personalizada en su lugar. No tuve que hacer ningún reemplazo de método ligeramente desagradable, y no tengo que perder el tiempo con las puntas cuando realmente solo quiero trabajar programáticamente.
Me gustaría ver el equivalente de +layerClass
dentro de UINavigationController +navigationBarClass
, pero por ahora esto funciona.