Set programmatically a custom subclass of UINavigationBar in UINavigationController

源自:http://stackoverflow.com/questions/1869331/set-programmatically-a-custom-subclass-of-uinavigationbar-in-uinavigationcontrol


问:

Does anyone know how can I use my custom subclass of UINavigationBar if I instantiateUINavigationController programmatically (without IB)?

Drag a UINavigationController in IB show me an under Navigation Bar and using Identity Inspectory I can change class type and set my own subclass of UINavigationBar but programmatically I can't, navigationBar property of Navigation Controller is readonly...

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.




回答1:

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.

Currently, as far as I know, the only way 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.

This is often fine, but sometimes using IB isn't really feasible.

So, I saw three options:

  1. Subclass UINavigationBar and hook it all up in IB, then muck about loading up the nib each time I wanted a UINavigationController,
  2. Use method replacement within a category to change the behaviour of UINavigationBar, rather than subclassing, or
  3. Subclass UINavigationBar and do a little mucking about with archiving/unarchiving the 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.

My approach was to create a 'template' archive of a UINavigationController, and unarchive that, returning it in initWithRootViewController.

Here's how:

In IB, I created a UINavigationController with the appropriate class set for the 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.

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).

Within initWithRootViewController I unarchive that template, and set self to the result of the 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];
}

Then, I can just grab a new instance of my UIViewController subclass which has the custom navigation bar set:

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.

I would like to see the equivalent of +layerClass within UINavigationController -+navigationBarClass - but for now, this works.


回答2:

Michael's solution works, but you can avoid NSKeyedArchiver and 'xxd' utility. Simply Subclass UINavigationController and override initWithRootViewController, loading your custom NavigationController NIB directly:

- (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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值