Reputation: 2481
I am subclassing UINavigationBar
. In my navigation bar I want to add a back button. Eventually I want to have a title a custom button and uicollectionview to look like this:
I am having trouble adding a button in my subclass of UINavigationBar
AppDelegate:
NFVDContentTableViewController *contentTVC = [[NFVDContentTableViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *contentNavCtr = [[UINavigationController alloc] initWithNavigationBarClass:[NFVDContentNavigationBar class] toolbarClass:[UIToolbar class]];
contentNavCtr.viewControllers = @[contentTVC];
In my Subclass UINavigation Header file:
@interface NFVDContentNavigationBar : UINavigationBar <UINavigationBarDelegate>
@end
In my Subclass UINavigationBar Implementation file:
@implementation NFVDContentNavigationBar
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
UINavigationItem* ni = [[UINavigationItem alloc] init];
UIButton *leftButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 38.0f, 29.0f)];
[leftButton setImage:[UIImage imageNamed:@"reveal-icon"] forState:UIControlStateNormal];
[leftButton addTarget:nil action:@selector(menuItemPressed:) forControlEvents:UIControlEventTouchUpInside];
[leftButton setContentMode:UIViewContentModeScaleAspectFit];
[leftButton setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin];
UIBarButtonItem *b =[[UIBarButtonItem alloc] initWithCustomView:leftButton];
ni.leftBarButtonItem = b;
self.items = @[ni];
}
- (CGSize)sizeThatFits:(CGSize)size{
CGSize newSize = [super sizeThatFits:size];
DLog(@"NewSize: %@", NSStringFromCGSize(newSize));
CGRect mainScreen = [UIScreen mainScreen].bounds;
if([UIView viewOrientationForSize:mainScreen.size] == ViewOrientationPortrait){
return CGSizeMake(newSize.width, 100);
}else if([UIView viewOrientationForSize:mainScreen.size] == ViewOrientationLandscape){
return CGSizeMake(newSize.width, 44);
}
return CGSizeZero;
}
- (void)layoutSubviews {
[super layoutSubviews];
}
@end
Upvotes: 2
Views: 697
Reputation: 4646
This is tough to do because UIViewController comes with its own UINavigationItems, see the docs here in the header file for UIViewController:
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer> {
@package
UIView *_view;
UITabBarItem *_tabBarItem;
UINavigationItem *_navigationItem;
You'd have to override this function of UIViewController which is possible but requires you to subclass UIViewController and then force all your view controllers in app to be subclasses of your subclassed view controller, so that's the hard way, I'd show the code for it, but it's sort of intense and a lot of code, but this is how I do it. Anyway, the better option is to do this in your ViewDidLoad of your UIViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem * tester = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"reveal-icon"] style:UIBarButtonItemStylePlain target:self action:@selector(menuItemPressed:)];
[[self navigationItem] setRightBarButtonItem:tester];
}
You can still subclass the UINavigationBar, but don't use it as a subclass to try to override the barbuttonitems already hooked to a UIViewController that comes as default from UIKit, if you want to get nuts about this, then here's the start of how you subclass a UIViewController, it's a lot of code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self == nil)
return nil;
if (self) {
_showsNotifications = false;
_showsAddFriends = false;
}
_notificationsBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage resizeImage:[UIImage imageNamed:@"YOUR IMAGE NAME"] height:27] style:UIBarButtonItemStylePlain target:nil action:nil];
_addFriendsBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"YOUR IMAGE NAME"] style:UIBarButtonItemStylePlain target:self action:@selector(addFriendsPressed)];
}
You see, you add to your custom view controller Bool properties that change the innards of the UINavigationItems in UIViewController. Using this method then requires you to call and set these bool values in your INIT of your viewcontrollers that are subclasses of this custom viewcontroller, do this like so:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
[self setShowsAddFriends:false];
[self setShowsNotifications:false];
}
return self;
}
Then in the subclassed UIViewController, make the bool switches able to actually switch.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (_showsNotifications) {
[[self navigationItem] setRightBarButtonItem:_notificationsBarButtonItem];
}
if (_showsAddFriends) {
[[self navigationItem] setRightBarButtonItem:_addFriendsBarButtonItem];
}
}
Done and done, complicated? Sort of, but this is how you template projects with as many custom Navigation item buttons as you want, the full code is about 1k lines of code, but this is the guts of it, subclass UIViewController, set bool properties in header, set these bool properties in your subclass so that the UIViewControllers that use this subclass as their parent class can call to these bool setter properties in "init", then you will be able to switch and choose whichever navigation items you like. In addition, to make sure that your view controllers maintain the items you set to them, make sure you also declare these item settings in your viewWillAppear in the view controllers that use your custom subclass of UIViewController as your parent view. Good luck, and have a good day.
In fact, I'll just show you the code, why? Because I'm bored and have too much to do with two app launches next week. So, here's the files you will need to look at, and keep in mind these are very much simplified:
CCUSTViewController.m
//******CCUSTViewController.m****
//this is the implementation file for the subclass of UIViewController
#import "CCUSTViewController.h"
#import "CCUSTFriendsViewController.h"
#import "CCUSTActivitiesViewController.h"
@interface CCUSTViewController ()
@end
@implementation CCUSTViewController
{
UIBarButtonItem * _notificationsBarButtonItem;
UIBarButtonItem * _addFriendsBarButtonItem;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self == nil)
return nil;
if (self) {
_showsNotifications = false;
_showsAddFriends = false;
}
_notificationsBarButtonItem = [[UIBarButtonItem alloc] initWithImage::[UIImage imageNamed:@"your imate"] style:UIBarButtonItemStylePlain target:self action:@selector(showNotificationsPressed)];
_addFriendsBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"your imate"] style:UIBarButtonItemStylePlain target:self action:@selector(addFriendsPressed)];
return self;
}
- (void)showNotificationsPressed
{
CCUSTActivitiesViewController * tobePushed = [CCUSTActivitiesViewController new];
[self navigationController] pushViewController:tobePushed animated:true];
}
- (void)addFriendsPressed
{
CCUSTFriendsViewController * tobePushed = [CCUSTFriendsViewController new];
[self navigationController] pushViewController:tobePushed animated:true];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (_showsNotifications) {
[[self navigationItem] setRightBarButtonItem:_notificationsBarButtonItem];
}
if (_showsAddFriends) {
[[self navigationItem] setRightBarButtonItem:_addFriendsBarButtonItem];
}
}
@end
CCUSTViewController.h
#import <UIKit/UIKit.h>
@interface CCUSTViewController : UIViewController
@property (nonatomic) BOOL showsNotifications;
@property (nonatomic) BOOL showsAddFriends;
@end
Then, here's an example of a subclasses View controller using your new navigation bar items switcher:
CCCUSTHomeViewController.h
#import "CCUSTViewController.h"
@interface CCCUSTHomeViewController : CCUSTViewController
@end
CCCUSTHomeViewController.m
// CCCUSTHomeViewController.m
#import "CCCUSTHomeViewController.h"
#import "CCCUSTHomeView.h"
@interface CCCUSTHomeViewController ()
@end
@implementation CCCUSTHomeViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
[self setShowsAddFriends:TRUE];
[self setShowsNotifications:TRUE];
}
return self;
}
-(void)loadView
{
[self setView:[CCCUSTHomeView new]];
}
-(CCCUSTHomeView*)contentView
{
return (id)[self view];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setTitle:@"HOME"];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setShowsAddFriends:TRUE];
[self setShowsNotifications:TRUE];
}
@end
Oh yes, and you need to add the same code from the init to the ViewWillAppear because when you pop the pushed view controller on the stack, you will need to intercept this even in your CCCUSTHomeViewController so that the UINavigationItems reappear as they are supposed to given the viewcontroller that is on the top the stack. You can also subclass everything from UICollectionViewController and UITableViewController doing the same method, this means that using this structure as a template, you can have an empty project in Xcode with all these subclasses added to it and then you will have more control over all your view controllers. There's much much much much more that you can do implementing this method, but some things, I must keep secret, but suffice it to say, a lot of big apps use this same technique and the additional advanced techniques that you won't see out in the wild. The neat thing is that you don't have to delegate, and you don't have manipulate much of anything. Best of luck, and have a good day.
GISTS because I care:
https://gist.github.com/anonymous/993e457561001cf8e77b
https://gist.github.com/anonymous/bc6095575038c36de77b
https://gist.github.com/anonymous/78b450d2a805781ab8a4
https://gist.github.com/anonymous/a0757179c17dd7078bb2
Upvotes: 2