Handling rotation of subclassed UINavigationBar with buttons - ios

I have subclassed UINavigationBar to have a custom navigation bar within my application. The bar is re-used on multiple view controllers with the same style Menu button as the left button item, which is why it is subclassed.
The subclass is then added to the View Controllers navigation bar in the storyboard.
This is the code within the UINavigationBar subclass:
- (void)drawRect:(CGRect)rect
{
// Drawing code
UIImage *image = [[UIImage imageNamed: #"mainNavBar"] resizableImageWithCapInsets:UIEdgeInsetsMake(0,5,0,5)];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
-(void)awakeFromNib {
UIButton *leftButton = [[UIButton alloc]initWithFrame:CGRectMake(7.0f, 7.0f, 38.0f, 29.0f)];
[leftButton setImage:[UIImage imageNamed:#"menuBarItem"] forState:UIControlStateNormal];
[leftButton addTarget:nil action:#selector(menuItemPressed:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:leftButton];
}
The issue is Rotation of the Device. I also have some code in appDidFinishLaunching using the appearance API for additional setup:
// Custom Navigation Bar appearance setup
[[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
[UIColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:245.0/255.0 alpha:1.0], UITextAttributeTextColor,
[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5],UITextAttributeTextShadowColor,
[NSValue valueWithUIOffset:UIOffsetMake(0, 1)],
UITextAttributeTextShadowOffset,nil]];
// Used to deal with the rotation of the nav bar when implemented outside of Navigation Controller
[[UINavigationBar appearance] setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight];
[[UINavigationBar appearance] setContentMode:UIViewContentModeScaleAspectFit];
// Used to push the title down slightly when in Landscape and NavBar outside of Navigation Controller
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];
When the device rotates it all works correctly in terms of the bar scaling in height as expected from 44 to 32px. However, the buttons still appear out side of the bar.
I have looked through some other SO posts on this, but cannot figure how to complete this correctly: iPhone: UINavigationBar with buttons - adjust the height
UINavigationBar autoresizing
Ideally I do not want to have to deal with the auto rotation in a View Controller because this UINavigationBar is re-used through a large amount of VCs. Adding in auto rotation code would mean each of the VCs would potentially need this too?
EDIT - following answer
If I change the awakeFromNib to include the following:
UINavigationItem* ni = [[UINavigationItem alloc] initWithTitle:#"Test"];
UIImage *menuBgImage = [UIImage imageNamed:#"menuBarItem"];
UIBarButtonItem *b =[[UIBarButtonItem alloc] initWithImage:menuBgImage landscapeImagePhone:menuBgImage style:UIBarButtonItemStylePlain target:nil action:#selector(menuItemPressed:)];
ni.leftBarButtonItem = b;
self.items = #[ni];
I have the issue of the UIBarButtonItemStylePlain ruining the image:
If I complete the previous setup with initWithCustomView and use the leftButton from the original code. With the leftButton using the autoresize for height- I then get a badly stretched image in landscape?
EDIT 2 - Additional image per answer

[self addSubview:leftButton];
That's the problem. This is not how you add things to a UINavigationBar! You must start with a UINavigationItem and set its buttons to a UIBarButtonItem. The bar button items will be adjusted when the navigation bar is resized.
Here is a simple example to get you started (could go in your awakeFromNib):
UINavigationItem* ni = [[UINavigationItem alloc] initWithTitle:#"Test"];
UIBarButtonItem* b = [[UIBarButtonItem alloc] initWithTitle:#"Howdy"
style:UIBarButtonItemStyleBordered
target:self action:#selector(pushNext:)];
ni.leftBarButtonItem = b;
self.items = #[ni];
However, note that this is not going to work if you then use this UINavigationBar as part of a UINavigationController! The left bar button will be pushed off by the view controller's navigationItem. If this is part of a standard navigation interface, just have every navigation controller add the left bar button item.
Also you should not be implementing drawRect. Set the nav bar's background image instead.

Related

How to make two different barTintColor present for one navigationBar in a pop/push transmition

Some normal code looks like this:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController.navigationBar setBarTintColor:someColor];
}
or
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController.navigationBar setBarTintColor: someColor];
}
However, this causes the navigation bar suddenly and completely change it's color upon push event occurs. (or upon you drag the left edge of a controller to pop it, which triggers 'viewWillDisappear' method) Is there any way that I can keep both color presenting during the pop animation?
I know weChat can do that...
As the photo below, while the controller at right side is being popped, the light gray background color (maybe background image) of it's navi bar is there, and the black color of the navi bar of the controller at left side is also there.
I solved it by making the the navigation bar transparent in both views, and I added the colors I want in the views itself with a rectangular view just for this to use as background color.
here the storyboard:
and here the final result:
Some code (make sure you don't hard code the width of the view like in this example):
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.translucent = YES;
UIView *bg = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 480, 60)];
bg.backgroundColor = [UIColor redColor];
[self.view addSubview:bg];
And make sure you disable "Adjust ScrollView Insets"

Buttons on navigation bar do not pick up window tint colour

Note, I've recently come back to this and it seems to have been fixed in more recent versions of the SDK, without me having to implement anything except the code in the question. Many thanks to all who answered.
I have an Objective-C app that runs on the iPad and displays a view controller with a modal presentation style of UIModalPresentationPageSheet:
UINavigationController *editorNavigationController = [[UINavigationController alloc] initWithRootViewController:editorViewController];
editorNavigationController.modalPresentationStyle = UIModalPresentationPageSheet;
[navigationController presentViewController:editorNavigationController animated:YES completion:nil];
When this view controller is displayed the buttons in the navigation bar are purple, which I assume has been picked up from the window's tint colour, which is what I want.
Later I need to display another view controller over the top, that fills the whole window:
UINavigationController *previewNavigationController = [[UINavigationController alloc]initWithRootViewController:myPreviewViewController];
[owningViewController presentViewController:previewNavigationController animated:YES completion:nil];
The problem I have is that when myPreviewController is displayed, the buttons in the navigation bar are grey. I've tried reinstating the colour on the new navigation controller:
previewNavigationController.navigationBar.tintColor = [UIColor colorWithRed:123/255.0 green:26/255.0 blue:69/255.0 alpha:1];
but without any joy.
How can I get the buttons to have the correct colour? Can I get the new navigation controller to pick up the window tint colour automatically, or do I have to set it explicitly? Is this something to do with presenting the second navigation controller over the top of one that uses UIModalPresentationPageSheet?
Any help much appreciated! Thanks,
Paul
You can set the navigationBar translucent and transparent.
In view Will Appear:
self.navigationController.navigationBar.translucent = NO;
Than create a UIView with frame size of navigationBar (CGRectMake(0,0,self.view.frame.size.height,22) and create buttons on it with color you need.
I know, thats a crutch but should work)
You can change the appearance of the UIBarButtonItem globally so all UINavigationControllers will share the same design:
[[UIBarButtonItem appearance] setTintColor:[UIColor purpleColor]];
[[UIBarButtonItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"fontName" size:16.0f],NSFontAttributeName,
nil] forState:UIControlStateNormal];
Additionally you could also change the [UINavigationBar appearance]:
//The setTintColor would tint the < Back button in the NavigationBar
[[UINavigationBar appearance] setTintColor:[UIColor greenColor]];
[[UINavigationBar appearance] setBarTintColor:[UIColor redColor]];
[[UINavigationBar appearance] setTitleTextAttributes:
#{NSForegroundColorAttributeName:[UIColor blueColor]}];
This code can be add it before presenting the UIViewControllers or simply in the AppDelegate.m.

How to change a custom NavigationItem title and background color?

Earlier I used a navigationcontroller with this code :
self.navigationItem.title = #"News";
Now I have the problem that I no longer needed the navigationcontroller as I am using a page controller for navigation. Now I added a navigationbar however it doesn't change the title with this code anymore.
Also how can I change the background color?
IN iOS 7 use write following code in didFinishLaunchingWithOptions
if ([[UINavigationBar class] respondsToSelector:#selector(appearance)])
{
[[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed: 4.0/255.0 green:173.0/255.0 blue:214.0/255.0 alpha:1.0f ]]; //// change background color of navigationBar
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]]; /// set backButton color of navigation bar
[[UINavigationBar appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor whiteColor]}]; // set title color
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], UITextAttributeTextColor,nil] forState:UIControlStateNormal]; /// set all barButton item color
self.navController.navigationBar.translucent = NO;// set translucent NO
}
Use as per your requirement.
So there is some magic happening when you use a navigation controller that you don't see and now that you've made the switch to justing using a navigation bar you lose that magic.
Navigation bars are responsible for managing and presenting UNavigationItems, and as such hold an array of them. A NavigationItem holds things like left and right buttons, and the title or titleView. When your navigation controller pushes a new controller, it creates a brand new UINavigation item then links it to your new view controller, and then pushes that onto navigation bars stack of nav items. That's why from within your view controller you set the title with self.navigationItem.title instead of referencing the navbar.
Basically you have to manage the bar and nav items yourself now. This should about do it for you:
_navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, yOffset, CGRectGetHeight(size), height)];
_navItem = [[UINavigationItem alloc] initWithTitle:#"My navbar title"];
[_navBar setItems:#[_navItem]];
[self.view addSubview:_navBar];
Of course you'll have to manage the size a bit differently as my example comes from an app where it's being used in landscape mode.

Back button strangely disappearing in UINavigationController but keeps working

Under iOS7 I've been experiencing an issue where the back button item will not show up if it has been set with a specific background image:
int imageSize = 21; //REPLACE WITH YOUR IMAGE WIDTH
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-400.f, 0)
forBarMetrics:UIBarMetricsDefault];
UIImage *barBackBtnImg = [[UIImage imageNamed:#"BackArrowDark.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, imageSize, 0, 0)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:barBackBtnImg
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
Upon doing this, any ViewController that I push in the navigation controller will have no back button item appearing, even though pressing where it should be, will make it appear, and any subsequent pushes of this view controller will have the button present on the screen.
This issue is only appearing under iOS7: everything works perfectly under iOS6.
Changing the back button completely with a leftBarButtonItem disables the back swipe, so that is not an option.
Any idea what I am doing wrong?
Thanks much for your consideration.
After trying different solutions, I found that changing the backIndicatorImage works best under iOS7, and it seems to be in line with the iOS7 interface paradigm:
[[UINavigationBar appearance] setTintColor:[UIColor grayColor]];
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault]; // Takes out title
UIImage *backButtonImage = [UIImage imageNamed:#"BackArrowDark.png"];
if ([UINavigationBar instancesRespondToSelector:#selector(setBackIndicatorImage:)]) {
[[UINavigationBar appearance] setBackIndicatorImage:backButtonImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:backButtonImage];
} else {
int imageSize = 21; // REPLACE WITH YOUR IMAGE WIDTH
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[backButtonImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, imageSize, 0, 0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
}
With this method:
When going back in the navigation controller, the back button item transition is the same as with the default indicator (a departure from the back button sliding away as well under iOS6);
Under iOS6, the backButton is changed and keeps its default iOS6 behaviour.
I'm happy!
Make sure you are not calling this in the view controller:
self.navigationController.navigationBar.tintColor = [UIColor redColor];
In iOS 7, this will tint the navigation bar but will also make your buttons invisible, yet functional just as you are describing.

Changing navigation bar image - not working

I'd like to change the background image (or color) of the navigation bar, but for some reason it's not working.
I've searched around, and found that on iOS 5.0+ (I'm running 6.1) this should work:
[self.navigationController.navigationBar setBackgroundImage:navigationBackgroundImage forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setBackgroundColor:[UIColor redColor]];
Now none of those work. Also tried with:
self.navigationController.navigationBar.backgroundColor = [UIColor redColor];
Now I'm thinking that I'm assigning it to the wrong object, but this works:
UIImageView *titleImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"scr1_title"]];
self.navigationItem.titleView = titleImage;
Use this code in your view controller to change the navigation bar background image.
if([[UINavigationBar class] respondsToSelector:#selector(appearance)]) //iOS >=5.0
{
UIImage * navBarImage = [UIImage imageNamed:#"image.png"] ;
[[UINavigationBar appearance]setBackgroundImage: navBarImage forBarMetrics:UIBarMetricsDefault];
}
You can set the tint color on the navigation bar of the navigation controller. Make sure you select the correct on as shown in the screenshot below.
Select your storyboard in the file browser.
Select the navigation controller in the storyboard.
In the Document Outline (second column) select the navigation bar of the navigation controller.
In the Utilities view (column on the right) adjust the tint color.
Click on the root view controller to the right on the navigation controller. It will pick up the new color after you click on it.
To set the image..
Write this code
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"navigationBar"] forBarMetrics:UIBarMetricsDefault];
inside
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
}

Resources