objective-c 以编程方式在 UINavigationController 中设置 UINavigationBar 的自定义子类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1869331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Set a custom subclass of UINavigationBar in UINavigationController programmatically
提问by Duccio
Does anyone know how can I use my custom subclass of UINavigationBarif I instantiate UINavigationControllerprogrammatically (without IB)?
有谁知道UINavigationBar如果我以UINavigationController编程方式实例化(没有 IB),我该如何使用我的自定义子类?
Drag a UINavigationControllerin IB show me an under Navigation Bar and using Identity Inspectory I can change class type and set my own subclass of UINavigationBarbut programmatically I can't, navigationBarproperty of Navigation Controller is readonly...
UINavigationController在 IB 中拖动 a向我显示导航栏下的一个并使用身份检查我可以更改类类型并设置我自己的子类UINavigationBar但以编程方式我不能,navigationBar导航控制器的属性是只读的...
What should I do to customize the navigation bar programmatically? Is IB more "powerful" than "code"? I believed all that can be done in IB could be done also programmatically.
我应该怎么做以编程方式自定义导航栏?IB 比“代码”更“强大”吗?我相信所有可以在 IB 中完成的工作也可以通过编程来完成。
回答by Fred
You don't need to muck with the XIB simply use KVC.
您无需使用 XIB,只需使用 KVC。
[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];
回答by Pascalius
Since iOS5, apple provides a method to do this directly. Reference
从iOS5开始,苹果提供了一种方法可以直接做到这一点。参考
UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];
回答by gorbster
As of iOS 4, you can use the UINibclass to help solve this issue.
从 iOS 4 开始,您可以使用UINib该类来帮助解决此问题。
- Create your custom
UINavigationBarsubclass. - Create an empty xib, add a
UINavigationControlleras the single object. - Set the class for the
UINavigationController'sUINavigationBarto your custom subclass. - Set your root view controller via one of these methods:
[navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];[navController pushViewController:myRootVC];
- 创建您的自定义
UINavigationBar子类。 - 创建一个空的 xib,添加一个
UINavigationController作为单个对象。 - 将
UINavigationController's的类设置UINavigationBar为您的自定义子类。 - 通过以下方法之一设置您的根视图控制器:
[navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];[navController pushViewController:myRootVC];
In code:
在代码中:
UINib *nib = [UINib nibWithNibName:@"YourCustomXib" bundle:nil];
UINavigationController *navController =
[[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];
Now you've got a UINavigationControllerwith your custom UINavigationBar.
现在你已经有了一个UINavigationController自定义的UINavigationBar.
回答by Michael Tyson
As far as I can tell, sometimes it is indeed necessary to subclass UINavigationBar, to do some nonstandard restyling. It's sometimes possible to avoid having to do so by using categories, but not always.
据我所知,有时确实需要对 UINavigationBar 进行子类化,以进行一些非标准的重新设计。有时可以通过使用类别来避免这样做,但并非总是如此。
Currently, as far as I know, the onlyway to set a custom UINavigationBar within a UIViewController is via IB (that is, via an archive) - it probably shouldn't be that way, but for now, we gotta live with it.
目前,据我所知,在UIViewController 中设置自定义 UINavigationBar的唯一方法是通过 IB(即通过存档)——它可能不应该是那样,但现在,我们必须接受它。
This is often fine, but sometimes using IB isn't really feasible.
这通常很好,但有时使用 IB 并不真正可行。
So, I saw three options:
所以,我看到了三个选项:
- Subclass UINavigationBar and hook it all up in IB, then muck about loading up the nib each time I wanted a UINavigationController,
- Use method replacementwithin a category to change the behaviour of UINavigationBar, rather than subclassing, or
- Subclass UINavigationBar and do a little mucking about with archiving/unarchiving the UINavigationController.
- 子类化 UINavigationBar 并将其全部挂在 IB 中,然后每次我想要 UINavigationController 时都在加载笔尖,
- 使用类别内的方法替换来改变 UINavigationBar 的行为,而不是子类化,或者
- 子类化 UINavigationBar 并在归档/取消归档 UINavigationController 方面做一些小事。
Option 1 was unfeasible (or at least too annoying) for me in this case, as I needed to create the UINavigationController programmatically, 2 is a little dangerous and more of a last-resort option in my opinion, so I chose option 3.
在这种情况下,选项 1 对我来说是不可行的(或者至少太烦人了),因为我需要以编程方式创建 UINavigationController,2 有点危险,在我看来更像是最后的选择,所以我选择了选项 3。
My approach was to create a 'template' archive of a UINavigationController, and unarchive that, returning it in initWithRootViewController.
我的方法是创建一个 UINavigationController 的“模板”存档,然后取消存档,将其返回到initWithRootViewController.
Here's how:
就是这样:
In IB, I created a UINavigationController with the appropriate class set for the UINavigationBar.
在 IB 中,我创建了一个 UINavigationController,并为 UINavigationBar 设置了适当的类。
Then, I took the existing controller, and saved an archived copy of it using +[NSKeyedArchiver archiveRootObject:toFile:]. I just did this within the app delegate, in the simulator.
然后,我使用现有的控制器,并使用+[NSKeyedArchiver archiveRootObject:toFile:]. 我只是在模拟器中的应用程序委托中执行此操作。
I then used the 'xxd' utility with the -i flag, to generate c code from the saved file, to embed the archived version in my subclass (xxd -i path/to/file).
然后我使用带有 -i 标志的“xxd”实用程序,从保存的文件生成 c 代码,将存档版本嵌入到我的子类 ( xxd -i path/to/file) 中。
Within initWithRootViewControllerI unarchive that template, and set self to the result of the unarchive:
在initWithRootViewController我解压该模板中,并将 self 设置为解压的结果:
// 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];
}
Then, I can just grab a new instance of my UIViewController subclass which has the custom navigation bar set:
然后,我可以获取我的 UIViewController 子类的一个新实例,它设置了自定义导航栏:
UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease];
[self.navigationController presentModalViewController:modalViewController animated:YES];
This gives me a modal UITableViewController with a navigation bar and toolbar all set up, and with the custom navigation bar class in place. I didn't need to do any slightly-nasty method replacement, and I don't have to muck about with nibs when I really just want to work programmatically.
这为我提供了一个模式 UITableViewController,其中包含所有设置好的导航栏和工具栏,以及自定义导航栏类。我不需要做任何稍微讨厌的方法替换,而且当我真的只想以编程方式工作时,我也不必操心笔尖。
I would like to see the equivalent of +layerClasswithin UINavigationController - +navigationBarClass- but for now, this works.
我想+layerClass在 UINavigationController 中看到等效的东西- +navigationBarClass- 但就目前而言,这是有效的。
回答by obb64
I use "option 1"
我使用“选项1”
Create a nib-file with only the UINavigationController in it. And set the UINavigationBar Class to my custom Class.
创建一个仅包含 UINavigationController 的 nib 文件。并将 UINavigationBar 类设置为我的自定义类。
self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject];
[navigationController pushViewController:rootViewController animated:YES];
回答by 100grams
Michael's solution works, but you can avoid NSKeyedArchiver and 'xxd' utility. Simply Subclass UINavigationController and override initWithRootViewController, loading your custom NavigationController NIB directly:
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;
}
回答by memmons
Update:Using object_SetClass()no longer works as if iOS5 GM. An alternate solution has been added below.
更新:使用object_SetClass()不再像 iOS5 GM 一样工作。下面添加了替代解决方案。
Use NSKeyedUnarchiver to manually set the unarchive class for the nav bar.
使用 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"];
Note: This original solution only works pre-iOS5:
注意:此原始解决方案仅适用于 iOS5 之前的版本:
There is a great solution, which I posted here-- inject navBar subclass directly into your view the UINavigationController:
有一个很好的解决方案,我在这里发布- 将 navBar 子类直接注入您的视图UINavigationController:
#import <objc/runtime.h>
- (void)viewDidLoad {
[super viewDidLoad];
object_setClass(self.navigationController.navigationBar, [MyNavBar class]);
// the rest of your viewDidLoad code
}
回答by Paul de Lange
These category methods are dangerous and not for novices. Also the complication with iOS4 and iOS5 being different, makes this an area that can cause bugs for many people. Here is a simple subclass that I use that supports iOS4.0 ~ iOS6.0 and is very simply.
这些分类方法很危险,不适合新手。此外,iOS4 和 iOS5 的复杂性不同,这使得这个区域可能会导致许多人出现错误。这是我使用的一个简单的子类,它支持 iOS4.0 ~ iOS6.0 并且非常简单。
.h
。H
@interface XXXNavigatioNBar : UINavigationBar
@end
.m
.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
回答by james
One scenario I've found that we need to use subclass rather than category is to set navigationbar backgroundcolor with pattern image, because in iOS5 overwriting drawRect using category does not work any more. If you want to support ios3.1-5.0, the only way you can do is to subclass navigationbar.
我发现我们需要使用子类而不是类别的一种情况是使用图案图像设置导航栏背景颜色,因为在 iOS5 中使用类别覆盖 drawRect 不再起作用。如果你想支持ios3.1-5.0,你唯一能做的就是继承navigationbar。
回答by off
If you want to subclass navBar just to change background image - there is no need to in iOS 5. There will be method like this one setBackgroundImage
如果你想子类化 navBar 只是为了改变背景图像 - 在 iOS 5 中没有必要。会有这样的方法setBackgroundImage

