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

Duc*_*cio 92 objective-c uinavigationbar uinavigationcontroller

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

拖动UINavigationController在IB中显示我在导航栏下并使用Identity Inspectory我可以更改类类型并设置我自己的子类UINavigationBar但编程方式我不能,navigationBar导航控制器的属性是只读...

我该怎么做才能以编程方式自定义导航栏?IB比"代码"更"强大"吗?我相信在IB中可以做的所有事情也可以通过编程方式完成.

小智 89

只需使用KVC,您就不需要使用XIB.

[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是hacky AF (8认同)
  • 你是如何找到导航控制器的这个关键路径@"navigationBar"的.你能分享一下吗? (6认同)

Pas*_*ius 66

从iOS5开始,apple提供了一种直接执行此操作的方法.参考

UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];
Run Code Online (Sandbox Code Playgroud)

  • 是的,它已经在iOS 6中添加了,但在iOS 5中也得到了支持.一位Apple工程师在WWDC 2012 Session 216中提到了这一点. (7认同)

小智 37

从iOS 4开始,您可以使用UINib该类来帮助解决此问题.

  1. 创建自定义UINavigationBar子类.
  2. 创建一个空的xib,添加一个UINavigationController作为单个对象.
  3. UINavigationControllers 的类设置为UINavigationBar自定义子类.
  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];
Run Code Online (Sandbox Code Playgroud)


现在你有了UINavigationController自定义UINavigationBar.

  • 我可以确认这种方式仍然适用于iOS 5 GM (4认同)
  • 恕我直言,这是制作这种有时不可避免的黑客的最不"黑客"方式. (3认同)
  • 实际上,有一个奇怪的问题.当我这样做时,新的navigationController的navigationItem没有被设置为分配给我正在推送到(空)堆栈的viewController的navigationItem属性. (2认同)

Mic*_*son 26

据我所知,有时确实有必要对UINavigationBar进行子类化,以进行一些非标准的重新定位.有时可以通过使用类别来避免这样做,但并非总是如此.

目前,据我所知,在UIViewController中设置自定义UINavigationBar 的唯一方法是通过IB(即通过存档) - 它可能不应该那样,但是现在,我们必须接受它.

这通常很好,但有时使用IB并不可行.

所以,我看到了三个选择:

  1. 子类UINavigationBar并将其全部挂在IB中,然后每当我想要一个UINavigationController时,关于加载nib的问题,
  2. 在类别中使用方法替换来更改UINavigationBar的行为,而不是子类化,或
  3. 子类UINavigationBar并对UINavigationController的归档/取消归档做了一些讨论.

在这种情况下,选项1对我来说是不可行的(或者至少太烦人),因为我需要以编程方式创建UINavigationController,2在我看来有点危险并且更多是最后的选择,所以我选择了选项3.

我的方法是创建一个UINavigationController的"模板"存档,并将其归档,将其返回initWithRootViewController.

这是如何做:

在IB中,我创建了一个UINavigationController,其中包含适用于UINavigationBar的类集.

然后,我使用现有的控制器,并使用保存了它的存档副本+[NSKeyedArchiver archiveRootObject:toFile:].我只是在app委托中,在模拟器中完成了这个.

然后我使用'xxd'实用程序和-i标志,从保存的文件生成c代码,将存档版本嵌入到我的子类(xxd -i path/to/file)中.

initWithRootViewController我取消归档该模板,并将self设置为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];
}
Run Code Online (Sandbox Code Playgroud)

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

UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease];
[self.navigationController presentModalViewController:modalViewController animated:YES];
Run Code Online (Sandbox Code Playgroud)

这给了我一个模态UITableViewController,其中包含一个导航栏和工具栏,并且具有自定义导航栏类​​.我不需要做任何稍微讨厌的方法替换,当我真的只想以编程方式工作时,我不必捣乱nib.

我想+layerClass在UINavigationController中看到相同的东西+navigationBarClass- 但是现在,这是有效的.


小智 5

我用"选项1"

创建一个只包含UINavigationController的nib文件.并将UINavigationBar类设置为我的自定义类.

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

[navigationController pushViewController:rootViewController animated:YES];
Run Code Online (Sandbox Code Playgroud)


100*_*ams 5

Michael的解决方案有效,但您可以避免使用NSKeyedArchiver和'xxd'实用程序.只需子类UINavigationController并覆盖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;
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*n S 0

建议UINavigationBar类进行子类化。自定义导航栏的首选方法是设置其属性以使其按您的意愿显示,并使用 UIBarButtonItems 内的自定义视图以及委托来获得所需的行为。

您想要做什么需要子类化?

另外,我不认为 IB 实际上正在取代导航栏。我很确定它只是不显示默认导航栏,而是将您的自定义导航栏作为子视图。如果您调用 UINavigationController.navigationBar,您会获得导航栏的实例吗?

  • 嗨,本,谢谢。我知道这不是子类 UINavigationBar 的更好方法,但我想设置一个覆盖 drawRect: 方法的背景图像。问题是,使用 IB 我可以更改 UINavigationController 中导航栏的类,但以编程方式我不能。是的,IB 实际上正在替换导航栏: NSLog(@"%@", self.navigationController.navigationBar); <自定义导航栏:0x1806160;基类 = UINavigationBar; 帧=(0 20;320 44);剪辑边界=是;不透明=否;自动调整大小=W;层 = <CALayer: 0x1806da0>> (2认同)