92 votes

Définir une sous-classe personnalisée de UINavigationBar dans UINavigationController de manière programmatique

Est-ce que quelqu'un sait comment je peux utiliser ma sous-classe personnalisée de UINavigationBar si j'instancie UINavigationController de manière programmatique (sans IB) ?

Faites glisser un UINavigationController dans l'IB, j'affiche une barre de navigation inférieure et, à l'aide d'Identity Inspectory, je peux modifier le type de classe et définir ma propre sous-classe de UINavigationBar mais par programme, je ne peux pas, navigationBar du contrôleur de navigation est en lecture seule...

Que dois-je faire pour personnaliser la barre de navigation de manière programmatique ? L'IB est-il plus "puissant" que le "code" ? Je pensais que tout ce qui pouvait être fait avec IB pouvait également être fait par programme.

0 votes

Avez-vous eu la chance de trouver une solution ailleurs ?

0 votes

Avez-vous reçu une réponse à ce sujet ?

89voto

Fred Points 669

Vous n'avez pas besoin de vous occuper du XIB, utilisez simplement le KVC.

[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];

0 votes

Cette solution fonctionne à merveille (du moins sous iOS 5.1). Toutes les autres solutions semblent représenter beaucoup plus de travail. Je cherche toujours l'inconvénient.

6 votes

Comment avez-vous trouvé ce chemin clé @"navigationBar" pour le contrôleur de navigation ? pouvez-vous nous en faire part ?

0 votes

Êtes-vous sûr que cela ne va pas entraîner le rejet de l'application ? Je ne l'ai vu nulle part en fait.

66voto

Pascalius Points 1202

Depuis iOS5, Apple fournit une méthode pour le faire directement. Référence

UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];

0 votes

@nonamelive En fait, non, ajouté dans iOS6 : developer.apple.com/library/ios/#releasenotes/General/

7 votes

Oui, elle a été ajoutée dans iOS 6, mais elle est également prise en charge dans iOS 5. Un ingénieur d'Apple l'a mentionné lors de la session 216 de la WWDC 2012.

37voto

gorbster Points 664

À partir d'iOS 4, vous pouvez utiliser la fonction UINib pour aider à résoudre ce problème.

  1. Créez votre propre UINavigationBar sous-classe.
  2. Créez un xib vide, ajoutez un UINavigationController comme l'unique objet.
  3. Définir la classe pour le UINavigationController 's UINavigationBar à votre sous-classe personnalisée.
  4. Définissez votre contrôleur de vue racine via l'une de ces méthodes :
    • [navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];
    • [navController pushViewController:myRootVC];

En code :

UINib *nib = [UINib nibWithNibName:@"YourCustomXib" bundle:nil];
UINavigationController *navController = 
             [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];

Maintenant vous avez un UINavigationController avec vos UINavigationBar .

0 votes

Mais alors, comment définir le rootViewController ?

0 votes

Vous utilisez [navcontroller setViewControllers :[NSArray arrayWithObject:<YOUR_ROOT_CONTROLLER>]].

0 votes

Désolé, vous utilisez [navcontroller pushViewController:<Votre_root_CONTROLLER> animated:NO]

26voto

Michael Tyson Points 1112

Pour autant que je sache, il est parfois nécessaire de sous-classer UINavigationBar pour effectuer un restylage non standard. Il est parfois possible d'éviter d'avoir à le faire en utilisant catégories mais pas toujours.

Actuellement, pour autant que je sache, le uniquement La façon de définir une UINavigationBar personnalisée au sein d'un UIViewController est via IB (c'est-à-dire via une archive) - il ne devrait probablement pas en être ainsi, mais pour l'instant, nous devons nous en accommoder.

C'est souvent très bien, mais il arrive que l'utilisation de l'IB ne soit pas vraiment possible.

Donc, j'ai vu trois options :

  1. Sous-classer UINavigationBar et accrocher tout ça dans IB, puis s'embêter à charger la nib chaque fois que je voulais un UINavigationController,
  2. Utilisez remplacement des méthodes dans une catégorie pour modifier le comportement de UINavigationBar, plutôt que de sous-classer, ou
  3. Sous-classez UINavigationBar et faites un peu de bricolage avec l'archivage/désarchivage de UINavigationController.

L'option 1 était irréalisable (ou du moins trop ennuyeuse) pour moi dans ce cas, car je devais créer le UINavigationController de manière programmatique, l'option 2 est un peu dangereuse et plus une option de dernier recours à mon avis, j'ai donc choisi l'option 3.

Mon approche a consisté à créer une archive "modèle" d'un UINavigationController, puis à la désarchiver, en la renvoyant dans le fichier initWithRootViewController .

Voici comment :

Dans IB, j'ai créé un UINavigationController avec la classe appropriée définie pour la UINavigationBar.

Ensuite, j'ai pris le contrôleur existant, et j'en ai sauvegardé une copie archivée en utilisant +[NSKeyedArchiver archiveRootObject:toFile:] . Je viens de le faire dans le délégué de l'application, dans le simulateur.

J'ai ensuite utilisé l'utilitaire 'xxd' avec le drapeau -i, pour générer du code c à partir du fichier sauvegardé, afin d'intégrer la version archivée dans ma sous-classe ( xxd -i path/to/file ).

Sur initWithRootViewController Je désarchive ce modèle, et mets self au résultat de la désarchivage :

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

Ensuite, je peux simplement saisir une nouvelle instance de ma sous-classe UIViewController qui a la barre de navigation personnalisée définie :

UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease];
[self.navigationController presentModalViewController:modalViewController animated:YES];

J'obtiens ainsi un UITableViewController modal avec une barre de navigation et une barre d'outils configurées, et avec la classe de barre de navigation personnalisée en place. Je n'ai pas eu besoin de procéder à un remplacement de méthode un peu méchant, et je n'ai pas à m'embêter avec des nibs lorsque je veux simplement travailler de manière programmatique.

J'aimerais voir l'équivalent de +layerClass au sein de UINavigationController - +navigationBarClass - mais pour l'instant, ça marche.

5voto

obb64 Points 61

J'utilise "l'option 1".

Créez un fichier nib contenant uniquement le UINavigationController. Et définissez la classe UINavigationBar avec ma classe personnalisée.

self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject];

[navigationController pushViewController:rootViewController animated:YES];

0 votes

Le nom approprié du fichier nib serait donc loadNibNamed:@"navigationController ? Vous récupérez en fait l'intégralité de navigationController dans la nib et pas seulement la barre. N'est-ce pas ?

0 votes

Cela fonctionne pour moi, mais lorsque je clique sur une option dans mon tableau, je perds le bouton retour.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X