¿Cómo se subclasifica UINavigationBar para un UINavigationController mediante progtwigción?

Estoy usando una función drawRect personalizada para dibujar en UINavigationBar en mi aplicación en iOS4, no usa imágenes, solo CoreGraphics.

Como no puede implementar drawRect en la categoría UINavigationBar en iOS5, Apple sugiere subclase UINavigationBar .

¿Cómo es posible reemplazar el UINavigationBar con mi subclase en UINavigationController (por lo que será compatible con iOS4 e iOS5) cuando la propiedad navigationBar es de solo lectura?

 @property(nonatomic, readonly) UINavigationBar *navigationBar 

No estoy usando ningún XIB en mi aplicación, así que agregar una UINavigationBar a una NIB y cambiar la clase a través de InterfaceBuilder no es una opción.

A partir de iOS6, ahora es bastante simple de lograr sin swizzling o jugar con otras clases utilizando el método initWithNavigationBarClass:toolbarClass:

 - (id)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass; 

De los documentos:

Inicializa y devuelve un controlador de navegación recién creado que usa sus subclases de barras personalizadas.

Respuesta actualizada para iOS6.

En iOS 6 agregaron un nuevo método para UINavigationController que también está disponible en iOS 5:

 - (id)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass; 

Ahora puede pasar su clase personalizada cuando se crea una instancia del controlador de navegación.

La única forma admitida de hacerlo en iOS 4 es utilizar el método de Interface Builder. No tiene que usar IB para hacer nada excepto establecer la subclase UINavigationBar (aún puede configurar toda la configuración de la vista mediante progtwigción).

 - (id)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass; 

Enfrenté un problema con el método anterior. Hay un método “initWithRootViewController” para inicializar UINavigationController. Pero, si uso “initWithNavigationBarClass” para iniciar el UINavigationController, entonces no hay forma de que pueda configurar “rootViewController” para el UINavigationController.

Este enlace El cambio de un controlador de vista de usuario de UINavigationController me ayudó a agregar un control de raíz de usuario después de que el UINavigationController se inicializa con “initWithNavigationBarClass”. Básicamente, el truco es subclase UINavigationController. Aunque todavía no lo he probado en IB, funciona bien en el código.

Tenía problemas con las respuestas 2-4 en iOS 4 (de la respuesta de AnswerBot), y necesitaba una forma de cargar el UINavigationController mediante progtwigción (aunque el método NIB funcionó) … Así que hice esto:

Creó un archivo XIB en blanco (no establezca el propietario del archivo), agregue un UINavigationController (asígnele la clase personalizada de UINavigationController), cambie el UINavigationBar a su clase personalizada (aquí es CustomNavigationBar), luego cree el siguiente encabezado de clase personalizado (CustomNavigationController.h en este caso):

 #import  @interface CustomNavigationController : UINavigationController + (CustomNavigationController *)navigationController; + (CustomNavigationController *)navigationControllerWithRootViewController:(UIViewController *)rootViewController; @end 

y implementación personalizada (CustomNavigationController.mm)

 #import "CustomNavigationController.h" @interface CustomNavigationController () @end @implementation CustomNavigationController + (CustomNavigationController *)navigationController { NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomNavigationController" owner:self options:nil]; CustomNavigationController *controller = (CustomNavigationController *)[nib objectAtIndex:0]; return controller; } + (CustomNavigationController *)navigationControllerWithRootViewController:(UIViewController *)rootViewController { CustomNavigationController *controller = [CustomNavigationController navigationController]; [controller setViewControllers:[NSArray arrayWithObject:rootViewController]]; return controller; } - (id)init { self = [super init]; [self autorelease]; // We are ditching the one they allocated. Need to load from NIB. return [[CustomNavigationController navigationController] retain]; // Over-retain, this should be alloced } - (id)initWithRootViewController:(UIViewController *)rootViewController { self = [super init]; [self autorelease]; return [[CustomNavigationController navigationControllerWithRootViewController:rootViewController] retain]; } @end 

Luego puede iniciar esa clase en lugar de un UINavigationController, y tendrá su barra de navegación personalizada. Si quieres hacerlo en un xib, cambia la clase de UINavigationController y UINavigationBar dentro de tu XIB.

Tipo de enmienda a las respuestas anteriores para aquellos que aún desean usar initWithRootViewController . Subclase UINavigationController y luego:

 - (id) initWithRootViewController:(UIViewController *)rootViewController { self = [super initWithNavigationBarClass:[CustomNavigationBar class] toolbarClass:nil]; if (self) { self.viewControllers = [NSArray arrayWithObjects:rootViewController, nil]; } return self; }