以编程方式在UINavigationController中设置UINavigationBar的自定义子类

有没有人知道我如何使用我的自定义UINavigationBar子类,如果我以编程方式实例化UINavigationController (没有IB)?

拖动一个UINavigationController在IB显示我一个导航栏下,并使用身份检验我可以改变类的types,并设置我自己的UINavigationBar的子类,但编程我不能导航控制器的navigationBar属性是只读…

我应该怎么做以编程方式自定义导航栏? IB比“代码”更“强大”吗? 我相信所有可以在IB中完成的工作也可以通过编程来完成。

你不需要用XB简单地使用KVC。

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

自iOS5以来,苹果提供了一个直接执行此操作的方法。 参考

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

从iOS 4开始,可以使用UINib类来帮助解决这个问题。

  1. 创build您自定义的UINavigationBar子类。
  2. 创build一个空的xib,添加一个UINavigationController作为单个对象。
  3. UINavigationControllerUINavigationBar的类设置为您的自定义子类。
  4. 通过以下方法之一设置您的根视图控制器:
    • [navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];
    • [navController pushViewController:myRootVC];

在代码中:

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

现在你已经有了一个UINavigationController和你自定义的UINavigationBar

据我所知,有时确实有必要inheritanceUINavigationBar,做一些非标准的重新列表。 有时可以通过使用类别来避免这样做,但并非总是如此。

目前,据我所知,在UIViewController中设置一个自定义UINavigationBar的唯一方法是通过IB(也就是通过一个存档) – 它可能不应该是这样,但现在,我们必须忍受它。

这通常很好,但有时使用IB并不是真的可行。

所以,我看到了三个select:

  1. 子类UINavigationBar并把它挂在IB中,然后在每次我想要一个UINavigationController的时候,
  2. 在类别中使用方法replace来改变UINavigationBar的行为,而不是子类化,或者
  3. 子类UINavigationBar,并做一些关于UINavigationController的归档/解除归档。

在这种情况下,对我来说选项1是不可行的(或者至less太讨厌),因为我需要以编程方式创buildUINavigationController,在我看来,2是一个有点危险,更多是最后的select,所以我select了选项3。

我的方法是创build一个UINavigationController的“模板”档案,并unarchive,在initWithRootViewController返回它。

就是这样:

在IB中,我创build了一个具有为UINavigationBar设置的适当类的UINavigationController。

然后,我拿着现有的控制器,并使用+[NSKeyedArchiver archiveRootObject:toFile:]保存它的存档副本。 我只是在模拟器中的应用程序代理内部完成的。

然后,我使用带有-i标志的“xxd”实用程序,从保存的文件生成c代码,将归档版本embedded到我的子类( xxd -i path/to/file )中。

initWithRootViewController我将该模板解压缩,并将其设置为unarchive的结果:

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

然后,我可以只抓住我的UIViewController子类的一个新的实例,它具有自定义的导航栏设置:

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

这给了我一个模式的UITableViewController与导航栏和工具栏都设置,并与自定义导航栏类​​的地方。 我不需要做任何稍微讨厌的方法replace,而且当我真的只是想以编程方式工作时,我不必费尽脑汁。

我想看看UINavigationController +navigationBarClass+layerClass的等价物,但现在,这个工作。

我使用“选项1”

创build一个只有UINavigationController的nib文件。 并将UINavigationBar类设置为我的自定义类。

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

迈克尔的解决scheme的工作原理,但你可以避免NSKeyedArchiver和'xxd'工具。 简单地inheritanceUINavigationController并覆盖initWithRootViewController ,直接加载自定义的NavigationController NIB:

 - (id) initWithRootViewController:(UIViewController *)rootViewController { [self release]; self = [[[[NSBundle mainBundle] loadNibNamed:@"CTNavigationController" owner:nil options:nil] objectAtIndex:0] retain]; [self setViewControllers:[NSArray arrayWithObject:rootViewController]]; return self; } 

更新:使用object_SetClass()不再像iOS5 GM一样工作。 下面添加了一个备用解决scheme。

使用NSKeyedUnarchiver手动设置导航栏的取消归档类。

  MyViewController *controller = [[[MyViewController alloc] init] autorelease]; NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:[NSKeyedArchiver archivedDataWithRootObject:controller]] autorelease]; [unarchiver setClass:[MyNavigationBar class] forClassName:@"UINavigationBar"]; controller = [unarchiver decodeObjectForKey:@"root"]; 

注意:这个原始的解决scheme只适用于iOS5之前:

有一个很好的解决scheme,我张贴在这里 – 直接注入navBar子类到你的视图UINavigationController

 #import <objc/runtime.h> - (void)viewDidLoad { [super viewDidLoad]; object_setClass(self.navigationController.navigationBar, [MyNavBar class]); // the rest of your viewDidLoad code } 

一个场景我发现我们需要使用子类而不是类别是设置导航栏背景颜色与模式图像,因为在iOS5覆盖drawRect使用类别不再工作。 如果你想支持ios3.1-5.0,你可以做的唯一方法是子类navigationbar。

这些类别的方法是危险的,而不是新手。 另外iOS4和iOS5的复杂性也不同,这使得这个地区可能会导致许多人的错误。 这里是我使用的支持iOS4.0〜iOS6.0的简单子类,非常简单。

。H

 @interface XXXNavigatioNBar : UINavigationBar @end 

.M

 #import "XXXNavigationBar.h" #import <objc/runtime.h> @implementation XXXNavigationBar - (void) didMoveToSuperview { if( [self respondsToSelector: @selector(setBackgroundImage:forBarMetrics:)]) { //iOS5.0 and above has a system defined method -> use it [self setBackgroundImage: [UIImage imageNamed: @"nav-bar"] forBarMetrics: UIBarMetricsDefault]; } else { //iOS4.0 requires us to override drawRect:. BUT!! //If you override drawRect: on iOS5.0 the system default will break, //so we dynamically add this method if required IMP implementation = class_getMethodImplementation([self class], @selector(iOS4drawRect:)); class_addMethod([self class], @selector(drawRect:), implementation, "v@:{name=CGRect}"); } } - (void)iOS4drawRect: (CGRect) rect { UIImage* bg = [UIImage imageNamed:@"nav-bar-blue"]; [bg drawInRect: rect]; } @end 

不build议 UINavigationBar类。 定制导航栏的首选方法是将其属性设置为使其显示为所需,并使用UIBarButtonItems中的自定义视图以及委托来获取所需的行为。

你想要做什么,需要子类化?

另外,我不认为IB实际上正在更换导航栏。 我敢肯定,它不是显示默认的,并有你的自定义导航栏作为子视图。 如果你调用UINavigationController.navigationBar,你有没有得到你的酒吧的实例?

如果你想子类navBar只是为了改变背景图像 – 没有必要在iOS 5中。会有像这样的一个setBackgroundImage

除了obb64的评论,我最终使用了setViewControllers:animated:技巧setViewControllers:animated:将控制器设置为从nib加载的navigationControllerrootController 。 以下是我正在使用的代码:

 - (void) presentModalViewControllerForClass: (Class) a_class { UINavigationController *navController = [[[NSBundle mainBundle] loadNibNamed: @"CustomNavBar" owner:self options:nil] lastObject]; LoginSignupBaseViewController *controller = [[a_class alloc] initWithNibName: nil bundle: nil]; controller.navigationController = navController; [navController setViewControllers: A(controller) animated: NO]; [self presentModalViewController: navController animated: YES]; [controller release]; }