Direct Answers:
- As suggested by @tc.'s answer, there is a disconnect somewhere between defining the view in a xib and using a View Controller (RightBarButtonItemVC) to define a custom view on a UIBarButtonItem, which is evident in the fact that UIViewControllerWrapperView receives the buttonPressed call instead of RightBarButtonItemVC. It looks like something is not being retained, although I'm not sure what.
- What follows is the specific working solution that I implemented. I did make a category, but not for UIViewControllerWrapperView as previously mentioned.
Specific Solution:
First create BarButtonItemLoader, an Objective-C category on UIViewController:
@interface UIViewController (BarButtonItemLoader)
In UIViewController+BarButtonItemLoader.h, define this method:
- (UIBarButtonItem *) rightBarButtonItem;
Since you can't keep track of state in a category, define a UIBarButtonItem in AppDelegate.h:
@property (strong, nonatomic) UIBarButtonItem *rightBarButtonItem;
Next, start implementing the category's rightBarButtonItem method by lazy loading the rightBarButtonItem from the AppDelegate (don't forget to #import "AppDelegate.h"). This ensures only one rightBarButtonItem will be created and retained in the AppDelegate:
- (UIBarButtonItem *) rightBarButtonItem {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.rightBarButtonItem) {
//create a rightBarButtonItem (see below)
}
return appDelegate.rightBarButtonItem;
}
Start assembling a UIView/UIBarButtonItem that will be set to the rightBarButtonItem. Transfer each element/configuration from the old Interface Builder / xib implementation. Most importantly take note of the frame information in the Size inspector so you can programmatically position your subviews just how you had them manually positioned in the .xib file.
- (UIBarButtonItem *) rightBarButtonItem {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.rightBarButtonItem) {
UIView *rightBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 264, 44)];
UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarView];
UIImageView *textHeader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"textHeader.png"]];
textHeader.frame = CGRectMake(2, 14, 114, 20);
[rightBarView addSubview:textHeader];
UIButton *button1 = [[UIButton alloc] initWithFrame:CGRectMake(100, 2, 70, 44)];
[button1 setImage:[UIImage imageNamed:@"button1.png"] forState:UIControlStateNormal];
[button1 setImage:[UIImage imageNamed:@"button1Highlighted.png"] forState:UIControlStateHighlighted];
[button1 addTarget:self action:@selector(button1Pressed) forControlEvents:UIControlEventTouchUpInside];
[rightBarView addSubview:button1];
UIButton *button2 = [[UIButton alloc] initWithFrame:CGRectMake(194, 2, 70, 44)];
[button2 setImage:[UIImage imageNamed:@"button2.png"] forState:UIControlStateNormal];
[button2 setImage:[UIImage imageNamed:@"button2Highlighted.png"] forState:UIControlStateHighlighted];
[button2 addTarget:self action:@selector(button2Pressed) forControlEvents:UIControlEventTouchUpInside];
[rightBarView addSubview:button2];
appDelegate.rightBarButtonItem = rightBarButtonItem;
}
return appDelegate.rightBarButtonItem;
}
Finally, implement the buttonXPressed methods in UIViewController+BarButtonItemLoader.m to your purpose:
- (void) button1Pressed {
NSLog(@"button1 Pressed");
}
- (void) button2Pressed {
NSLog(@"button2 Pressed");
}
...
Use the category by adding this code to any UIViewController or subclass thereof:
#import "UIViewController+BarButtonItemLoader.h"
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [self rightBarButtonItem];
}
Summary
This approach allows you to add a UIBarButtonItem on-the-fly to any UIViewController. The drawback is that you must add the above code to all UIViewControllers you create.
Another Option
If you want to further encapsulate the addition of UIBarButtonItems (or anything else), avoiding the need to add code in each View Controller, you should create a BaseViewController from which you then subclass all of your other View Controllers. From there you can consider other items that you want to include in all your View Controllers. Choosing the Category or Subclass route then becomes a question of granularity.