The image I used for background of navigation bar is 640x88. The code for navigation bar is below. It works well for iOS4.3 simulator, but the background image is shown twice as normal on iOS 5 simulator. How can I resize the background image on iOS 5.0?
#implementation UINavigationBar (CustomBackground)
- (UIImage *)barBackground {
[self setTintColor:[UIColor colorWithRed:0.5f
green: 0.25f
blue:0.09f
alpha:0.6]];
UIImage *image;
image=[UIImage imageNamed:#"audioNav.png"];
return image;
}
- (void)didMoveToSuperview {
//iOS5 only
if ([self respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)])
{
[self setBackgroundImage:[self barBackground] forBarMetrics:UIBarMetricsDefault];
}
}
- (void)drawRect:(CGRect)rect {
[[self barBackground] drawInRect:rect];
}
Create 2 images, one for normal resolution display, one for retina display:
audioNav.png - 320x44
audioNav#2x.png - 640x88
When using [UIImage imageNamed:#"audioNav.png"] it will always load the correct image.
Related
I'm having some difficulty getting the transparent bits of my png image that I'm using to replace the default status bar to be rendered as transparent in iOS 4.3. At the moment they are coming up black.
Here is the code I'm using to draw my image:
#implementation UINavigationBar (BackgroundImage)
- (void)drawRect:(CGRect)rect
{
UIImage *image = nil;
switch(self.tag)
{
case HeaderBG_Logo:
image = [UIImage imageNamed: #"top_bar_logo.png"];
break;
case HeaderBG_Plain:
image = [UIImage imageNamed: #"top_bar.png"];
break;
}
if(image != nil)
{
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
}
#end
Here is a side by side comparison of the same app running in the simulator in ios4.3 and ios5 (the image has a drop shadow as its bottom border):
As an aside, the 4.3 background image doesn't seem to be rendered as tall as the 5.0 one either.
I've tried setting the UINavigationBar to opaque / setting its background color to clearColor. None of that worked. :(
Can anyone help?
I ended up using the solution from How to create UINavigationBar drop shadow to add a shadow to the UINavigationBar instead.
Just an hint, for iOS 5 you could use the new appreance property to set the background image:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"top_bar.png"] forBarMetrics:UIBarMetricsDefault];
This will set the image for all UINavigationBars in the app.
I am not sure to understand if you are concerned by the status bar or by the navigationBar. According to you question it seems you are more concerned by the Navigation so here is a sample code that worked for me to add a background image for iOS5 and iOS4. It works with transparent png.
Just add this in the loadView method of your viewController:
#define kBackgroundImageTag 42
UINavigationBar *theNavigationBar = self.navigationController.navigationBar;
UIImage *myBackgroundImage = [UIImage imageNamed:#"myImage.png"];
if([theNavigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)]) //iOS5 stuff
{
[theNavigationBar setBackgroundImage:myBackgroundImage forBarMetrics: UIBarMetricsDefault];
}
else if(![theNavigationBar viewWithTag:kBackgroundImageTag]) //iOS 4 stuff
{
UIImageView *imageView = [[UIImageView alloc] initWithImage:myBackgroundImage];
imageView.tag = kBackgroundImageTag;
[theNavigationBar insertSubview:imageView atIndex:0];
}
Hope it helps !
Have a little design issue after having upgraded to iOS 5 and Xcode 4.2
This is how my view looked in iOS 4:
1 http://casperslynge.dk/1
And this is how it looks like in iOS 5:
2 http://casperslynge.dk/2
In my navigation delegate I have the following method to draw the "image" at the top:
- (void)drawRect:(CGRect)rect {
UIImage *image;
if(self.barStyle == UIBarStyleDefault){
image = [UIImage imageNamed: #"topbar_base.png"];
}
else{
image = [UIImage imageNamed: #"nyhedsbar_base.png"];
}
[image drawInRect:CGRectMake(-1, -1, self.frame.size.width+3, self.frame.size.height+3)];
}
And inside my controller I set the following:
self.navigationBarStyle = UIBarStyleBlack;
How come it is not working in iOS 5?
Thanks
Under iOS5, you need to use UIAppearance. Have a look at that. Here's an example for using it conditionally so that you can continue to support iOS4:
// iOS5-only to customize the nav bar appearance
if ([[UINavigationBar class] respondsToSelector:#selector(appearance)]) {
UIImage *img = [UIImage imageNamed: #"NavBarBackground.png"];
[[UINavigationBar appearance] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
}
As you can see, this sets a custom background image for all UINavigationBars. There are lots of things you can do with UIAppearance. You'll want to keep any custom stuff you're currently doing in drawRect: since pre-iOS4 devices will still use that and not the new UIAppearance code.
I've overrided(placed in category, or swizzled) UINavigationBar's drawRect to show custom background. In iOS 5 it's not working. What should I do?
Setting custom background for UINavigationBar to support iOS5 and iOS4 too!
http://www.mladjanantic.com/setting-custom-background-for-uinavigationbar-what-will-work-on-ios5-and-ios4-too/
http://rogchap.com/2011/06/21/custom-navigation-bar-background-and-custom-buttons/
As you know, until iOS 5 came out, we used drawRect override in AppDelegate to customize UINavigationBar.
But know, iOS 5 give us some new method for styling (and old doesn’t work).
How to build app that will work on iOS 4 and iOS 5 with stylized UINavigationBar?
You must to do both!
In AppDelegate use this code:
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *img = [UIImage imageNamed:#"navbar.png"];
[img drawInRect:rect];
}
#end
and in viewDidLoad method for iOS5 (in your view implementation):
if ([self.navigationController.navigationBar respondsToSelector:#selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"navbar.png"] forBarMetrics:UIBarMetricsDefault];
}
If you see, here we are asking if navbar will respondToSelector to avoid crash on iOS4!
Here's a less-ugly solution that works for both iOS4 and 5:
#implementation UINavigationBar (CustomBackground)
- (UIImage *)barBackground
{
return [UIImage imageNamed:#"top-navigation-bar.png"];
}
- (void)didMoveToSuperview
{
//iOS5 only
if ([self respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)])
{
[self setBackgroundImage:[self barBackground] forBarMetrics:UIBarMetricsDefault];
}
}
//this doesn't work on iOS5 but is needed for iOS4 and earlier
- (void)drawRect:(CGRect)rect
{
//draw image
[[self barBackground] drawInRect:rect];
}
#end
Try to read iOS 5.0 Release Notes
In iOS 5, the UINavigationBar, UIToolbar, and UITabBar implementations have changed so that the drawRect: method is not called unless it is implemented in a subclass. Apps that have re-implemented drawRect: in a category on any of these classes will find that the drawRect: method isn't called. UIKit does link-checking to keep the method from being called in apps linked before iOS 5 but does not support this design on iOS 5 or later.
There's some possible solutions:
Quickest fix For laziest of us :
#interface MyNavigationBar : UINavigationBar
#end
#implementation MyNavigationBar
- (void)drawRect:(CGRect)rect {
}
#end
#implementation UINavigationBar (BecauseIMLazyHacks)
/*
Another Ugly hack for iOS 5.0 support
*/
+ (Class)class {
return NSClassFromString(#"MyNavigationBar");
}
-(void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.frame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0, 0,
self.frame.size.width, self.frame.size.height), barBackground.CGImage);
}
#end
Again. It works, but You shouldn't do it like this.
Another way, as suggested in WWDC'11 is to override UINavigationBar (Create MyNavigationBar) and initialize UINavigationController from xib like here :
http://www.iosdevnotes.com/2011/09/custom-uinavigationbars-techniques/
And finally, use logic flow switch for iOS5.0 and iOS5.0-
Use new API where it's possible.
Categories is wrong path, Swizzling is wrong path. (They're just whispering in your ears:"Give yourself to the Dark Side. It is the only way you can save your apps.")
#implementation UINavigationBar (MyCustomNavBar)
- (void)setBackgroudImage:(UIImage*)image
{
CGSize imageSize = [image size];
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, imageSize.height);
UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:image];
backgroundImage.frame = self.bounds;
[self addSubview:backgroundImage];
[backgroundImage release];
}
#end
The above swizzling will allow you to set any custom background image for the UINavigationBar(iOS5 & iOS4).
Follow this link to make your code compatible with iOS4, 5 and 6.
You just have to make in Photoshop or other software a rectangular with the size of 320x44 or 640x88 (for retina display) and import it to your project
In AppDelegate use this code (in the header between #import and #implementation AppDelegate):
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed:#"top_bar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
In viewDidLoad use this code for iOS5 and iOS6:
#if defined(__IPHONE_5_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0
if ([self.navigationController.navigationBar respondsToSelector:#selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"top_bar.png"] forBarMetrics:UIBarMetricsDefault];
}
#endif
After iOS 5 - (void)drawRect:(CGRect)rect is not called while we create category for UINavigationBar but you can call -(void)awakeFromNib and add all the code that you want to add.
This code works fine with iPad Simulator 4.2, but not with later version of iOS4.3 or after that.I am not able to Override the UIToolbar class methods.
#implementation UIToolbar (CustomImage)
- (void)drawRect:(CGRect)rect
{
UIImage *image = [[UIImage imageNamed:#"ToolBar.png"] retain];
[image drawInRect:rect];
[image release];
}
//return 'best' size to fit given size. does not actually resize view. Default is return existing view size
- (CGSize)sizeThatFits:(CGSize)size {
CGSize result = [super sizeThatFits:size];
result.height = 80;
return result;
};
What would be the alternate solution for this ?Please guide me.
In later version ..- (void)drawRect:(CGRect)rect is never called .
Running with iPad Simulator 4.2 code works fine but with iPad Simulator 4.3 drawRect in never called.
Below is the Screenshot of Toolbar:
What about something like this?
#implementation UIToolbar (UIToolbarCategory)
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
if([self isMemberOfClass: [UIToolbar class]]){
[super drawRect:rect];
UIImage *image = [UIImage imageNamed:#"bar_gradient.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
}
#end
you are implementing it as a category, you need to subclass UIToolBar based on the iOS5 change log
In the iOS 5 beta, the UINavigationBar,
UIToolbar, and UITabBar implementations have changed so that the
drawRect: method is not called on instances of these classes unless it
is implemented in a subclass.
Apps that have re-implemented drawRect: in a category on any of these
classes will find that the drawRect: method isn’t called.
UIKit does link-checking to keep the method from being called in apps
linked before iOS 5 but does not support this design on iOS 5 or
later. Apps can either:
Use the customization API for bars that in iOS 5 and later, which is the preferred way.
Subclass UINavigationBar (or the other bar classes) and override drawRect: in the subclass.
I have a custom category to define a special background image for my navigationbar depending on the view I'm in.
Within the category I set the image using
- (void)drawRect:(CGRect)rect
The thing is, when navigating through my views in the Navigation Controller, the custom image disappears leaving a blank white space 320x44(size of the bar) until the next controller reappers - i.e. after about 1 second.
This sort of looks ugly and I was wondering whether there was a better way to do this so I have a clean implementation which looks nice aswell and fits my demands.
Thanks in advance.
Category Code:
- (void)initImageDictionary
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
}
- (void)drawRect:(CGRect)rect
{
UIImage *imageName = [navigationBarImages objectForKey:[NSValue valueWithNonretainedObject: self]];
UIImage *image = imageName;
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
- (void)setMyImage:(UIImage*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject: self]];
[self setNeedsDisplay];
}
Then I call:
[self.navigationBar performSelectorInBackground:#selector(setMyImage:) withObject:[UIImage imageNamed:imageTopbar]];
You should use UIKit only on the main thread.
In your ViewController, try this where #"BGimage.png" is your image file:
//puts an image as bg pattern(tiles) for a View
//self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"BGimage.png"]];