UINavigationBar setBackgroundImage: forBarMetrics: Not Working - objective-c

I just switched over to iOS 5 and everything appears to be working in my application aside from the custom navigation bar. I looked around and followed everybody's suggestion of calling the new methods setBackgroundImage: forBarMetrics: however it doesn't appear to work. This is the code I've tried to place both within the app delegate and within the viewDidLoad method of some of the view controllers:
UINavigationBar *nb = [[UINavigationBar alloc]init];
if( [nb respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)] )
{
UIImage *image = [UIImage imageNamed:#"navBarBackground.png"];
[nb setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
}
[nb release];
Unfortunately this doesn't work. If anybody has any suggestions at all, I'm all ears!

To apply image to all your navigation bars, use the appearance proxy:
[[UINavigationBar appearance] setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
For an individual bar:
// Assuming "self" is a view controller pushed on to a UINavigationController stack
[self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
In your example, the background image won't change because nb isn't hooked up to anything.

Answer by rob is correct but if application runs on iOS 4.3 or lower app will crash. So you can implement this like
if([[UINavigationBar class] respondsToSelector:#selector(appearance)]) //iOS >=5.0
{
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"navigationBar.png"] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"navBar-Landscape.png"] forBarMetrics:UIBarMetricsLandscapePhone];
}
This will set image for both mode Landscape and Portrait

You need to get the navigation bar from a navigation controller. Right now you are just creating one and then it deallocs when you release it. You need to get the navigation controller for your view controller.
UINavigationController * navigationController = [self navigationController];
UINavigationBar * nb = [navigationController navigationBar];

Note that if you want to apply the same to a toolbar, it is slightly different. Here's an example, building on Rob's answer:
[[UIToolbar appearance] setBackgroundImage:[UIImage imageNamed:#"NavBarDarkWoodGrain.png"] forToolbarPosition:UIToolbarPositionBottom barMetrics:UIBarMetricsDefault];

You didn't do something like use a UINavigationBar subclass? If you do this and override drawRect: on iOS5 and above, things will break.
See my answer here for a way to support both OS versions.

This worked for me.
UIView *parentViewLeft = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
parentViewLeft.backgroundColor = [UIColor clearColor];
if ([self.navigationController.navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)]) {
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"navbar.png"]
forBarMetrics:UIBarMetricsDefault];
} else {
UIImageView* imgBG = [[UIImageView alloc]init];
imgBG.image = [UIImage imageNamed:#"navbar.png"];
imgBG.frame = CGRectMake(-10,0, 330, 44);
[parentViewLeft addSubview:imgBG];
}
UIBarButtonItem *customBarButtomLeft = [[UIBarButtonItem alloc] initWithCustomView:parentViewLeft];
self.navigationItem.leftBarButtonItem = customBarButtomLeft;

Related

Navigationbar background image not fill navigationbar Objective-C

I need to add background image to navigationbar, I done the code but it shows below the status bar.How to display the image fully on navigationbar.(iOS 8).
What actually I need to achieve is like the image below
The code I used for navigationbar background image is
UIImage *navBarBackgroundImage = [UIImage imageNamed:#"NavigationBarImg"];
[[UINavigationBar appearance] setBackgroundImage:navBarBackgroundImage forBarMetrics:UIBarMetricsDefault];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
Make UINavigationBar transparent
[self.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault];
self.navigationBar.shadowImage = [UIImage new];
self.navigationBar.translucent = YES;

Subclass UINavigationbar for customise in iOS7 does not work

I have made a subclass of the navigation bar.
In there I set a background image and I would like to disable the clipping, because my Image has some shadow and an arrow which is higher than the navigation bar.
in iOS 6&5 it works perfectly when I remove the tick Clip Subviews in the Interface Builder in Xcode.
In iOS7 unfortunately the background image is always clipped...
I also tried to add the following lines:
- (void) awakeFromNib
{
// Initialization code
UIImage *image = [UIImage imageNamed:#"tt_navigation_bar.png"];
if ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) image = [UIImage imageNamed:#"tt_navigation_bar-ios7.png"];
[[UINavigationBar appearance] setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
NSDictionary *textTitleOptions = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:#"HelveticaNeue-Bold" size:21.0], UITextAttributeFont, [UIColor colorWithRed:255.0/255.0 green:109.0/255.0 blue:36.0/255.0 alpha:1], UITextAttributeTextColor, [UIColor clearColor], UITextAttributeTextShadowColor, nil];
[[UINavigationBar appearance] setTitleTextAttributes:textTitleOptions];
for (UIView *v in [self subviews]) {
NSLog(#"v: %#", v);
v.layer.masksToBounds = NO;
}
self.layer.masksToBounds = NO;
}
I solved this by manually editing my two images into the top n pixels and then "the rest" and setting the image of "the rest" to the shadowImage.

Change navigation button color in MFMailComposerViewController on iOS 7

I'm trying to change the text color for navigation buttons in a MFMailComposerViewController but it doesn't work like on iOS 6. In iOS 6 it worked with UIAppearance like this:
// Navigation button
UIBarButtonItem *barButton = [UIBarButtonItem appearance];
NSDictionary *barButtonTitleTextAttributes = #{UITextAttributeTextColor: [UIColor redColor]};
NSDictionary *disabledBarButtonTitleTextAttributes = #{UITextAttributeTextColor: [UIColor grayColor]};
[barButton setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];
[barButton setTitleTextAttributes:disabledBarButtonTitleTextAttributes forState:UIControlStateDisabled];
[barButton setBackgroundImage:[[UIImage imageNamed:#"btn_appearance"] stretchableImageWithLeftCapWidth:6 topCapHeight:0] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
But this doesn't work on iOS 7 and looks always like this:
I also tried to set the tintColor attribute on the navigationBar but this has no effect either:
navigationBar.tintColor = [UIColor redColor];
Is there anyway to change the navigation button text color in a MFMailComposeViewController on iOS 7?
I used this and works perfect in iOS7+
MFMailComposeViewController* mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.mailComposeDelegate = self;
[mailViewController setToRecipients:#[#"email#apple.com"]];
[mailViewController.navigationBar setTintColor:[UIColor orangeColor]];
[self presentViewController:mailViewController animated:YES completion:nil];
Like OemerA says - there is no way to change the colors. My problem was that in UIAppearance I set the background color of the bar to blue so then the "buttons" are no longer visible. Since the email is actually not part of your app it makes more sense to reset the nag bar appearance prior to creating the mail composer. This is how I do it:
// set to normal white
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
[[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor blackColor], NSForegroundColorAttributeName, nil]];
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
// set to back to blue with white text
[[UINavigationBar appearance] setBarTintColor:[UIColor blueColor]];
[[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], NSForegroundColorAttributeName, nil]];
If you set the tintColor on UIWindow it works perfectly fine, the first time presenting the MFMailComposerViewController. It seems like it loses the tintColor information for the subsequent calls.
Note: this changes the tint of every element of your window.
If you ever inspect the view hierarchy created by showing a MFMailComposeViewController you'll see that it's contained within an instance of _UITextEffectsRemoteView. You have zero programmatic access to any subviews of that, which I'm guessing is because they're probably owned by a separate process. Those subviews will inherit anything set on the various UIAppearance proxies (e.g., bar background, titleTextAttributes, etc) but nothing more.
The UIAppearance protocol does not mention this in the documentation, but it does have this in the comments of the header file:
Note for iOS7: On iOS7 the tintColor property has moved to UIView, and now has special inherited behavior described in UIView.h.
This inherited behavior can conflict with the appearance proxy, and therefore tintColor is now disallowed with the appearance proxy.
So the end result is that while you can control most aspects of the appearance of MFMailComposeViewController you will always get the system default blue tint color.
Bug report: http://openradar.appspot.com/radar?id=6166546539872256
As has been pointed out a few times, setting the tint on the MFMailComposeViewController navigation bar does not work. If you have other appearance changes set for the whole app, the tint colour is only one aspect of the issue, in our app we have changed the bar colour and UIBarButton text size so in the MFMailComposeViewController we see this:
The appearance in our app is set in a StyleGuide class with a function configureAppearanceModifiers called from the AppDelegate.
Taking the example from #timosdk I have added a second method:
- (void)neutraliseAppearanceModifiers {
[[UINavigationBar appearance] setTranslucent:NO];
[[UINavigationBar appearance] setTintColor:nil];
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
[[UINavigationBar appearance] setBackIndicatorImage:nil];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:nil];
[[UINavigationBar appearance] setBackgroundImage:nil
forBarPosition:UIBarPositionAny
barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setTitleTextAttributes:nil forState:UIControlStateNormal];
[[UIBarButtonItem appearance] setTitleTextAttributes:nil forState:UIControlStateHighlighted];
[[UIBarButtonItem appearance] setTitleTextAttributes:nil forState:UIControlStateDisabled];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:nil forState:UIControlStateNormal];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:nil forState:UIControlStateHighlighted];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:nil forState:UIControlStateDisabled];
}
I call this before initialising the MFMailComposeViewController and then call configureAppearanceModifiers again in the didFinishWithResult delegate before dismissing the ViewController, this works perfectly.
Swift 3.0
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.navigationBar.tintColor = UIColor.red
mail.mailComposeDelegate = self
mail.setToRecipients(["abc#abc.com"])
mail.setMessageBody("<p>You're so awesome!</p>", isHTML: true)
present(mail, animated: true)
} else {
// show failure alert
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}

change background image of navigation bar based on orientation

Is there a way to change the background image of navigation bar based on orientation? I tried using methods in the app delegate such as :
-(void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(orientation == (UIInterfaceOrientationMaskPortrait)||orientation==(UIInterfaceOrientationMaskPortraitUpsideDown)){
UIImage *myImage = [UIImage imageNamed:#"borderP.png"];
UIImage * imageBottom = [UIImage imageNamed:#"bottomP.png"];
[[UIToolbar appearance] setBackgroundImage:imageBottom forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:myImage forBarMetrics:UIBarMetricsDefault];
[[UIToolbar appearance] setBackgroundImage:imageBottom forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
NSLog(#"******P******");
}
else if(orientation == (UIInterfaceOrientationMaskLandscapeLeft||orientation== UIInterfaceOrientationMaskLandscapeRight)){
UIImage *myImage = [UIImage imageNamed:#"borderL.png"];
UIImage * imageBottom = [UIImage imageNamed:#"borderL.png"];
[[UIToolbar appearance] setBackgroundImage:imageBottom forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:myImage forBarMetrics:UIBarMetricsDefault];
[[UIToolbar appearance] setBackgroundImage:imageBottom forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
NSLog(#"******L********");
}
}
but the back ground image does not change.
Any advice?
The appearance proxy only applies only to new instances. To modify an instance that already exists, you you need to call setBackgroundImage:forToolbarPosition:barMetrics: on it directly.

Could not set Image on Navigation bar only in "ios 5.0"

In my universal app,
I am setting an image on navigation bar...using **
objective c category...on UINavigationBar
**
The code works fine in ios 5.0 in iphone
*The code works fine in ios 4.3 in iPhone/iPad*
*But not working in **ios 5.0 iPad***
- (void) drawRect:(CGRect)rect
{
UIImage *image;
image = [UIImage imageNamed: #"Navigation.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[self setTintColor:[UIColor clearColor]];
NSLog(#"Draw Rect");
}
To set a BG image for navigation bar in iOS 5 you can use the below code
if ([self.navigationController.navigationBar respondsToSelector:#selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"Navigation.png"] forBarMetrics:UIBarMetricsDefault];
}
But please keep in mind that this wont work in iOS 4. To make it work in both you also need to add a category to UINavigationBar
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *img = [UIImage imageNamed:#"Navigation.png"];
[img drawInRect:rect];
}
If you want to set the background application-wide you can use iOS5's UIAppearance.
[[UINavigationBar appearance] setBackgroundImage:X forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:X forBarMetrics:UIBarMetricsLandscape];