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.
Related
There are a few tutorial about showing how to add a shadow to a UINavigation bar, but is there any method which would best suit adding this shadow application wide, rather than in a single instance?
Or is my only option to simply have a sub-classed nab bar in every single view of my application? Thought there might be a quicker, easier way than doing that?
Thanks.
Create a category of UINavigationBar called UINavigationBar+dropshadow.m and put this in the file
#import <QuartzCore/QuartzCore.h>
#interface UINavigationBar (dropshadow)
-(void) applyDefaultStyle;
#end
#implementation UINavigationBar (dropshadow)
-(void)willMoveToWindow:(UIWindow *)newWindow{
[super willMoveToWindow:newWindow];
[self applyDefaultStyle];
}
- (void)applyDefaultStyle {
// add the drop shadow
self.layer.shadowColor = [[UIColor blackColor] CGColor];
self.layer.shadowOffset = CGSizeMake(0.0, 3.0);
self.layer.shadowOpacity = 0.25;
self.layer.shouldRasterize = YES;
}
#end
If you're working with iOS6, you can use the Appearance proxy to do that.
Here's the Apple Class Reference: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html
EDIT 1 Fixed required iOS version (was mistakenly iOS5 previously)
EDIT 2
See this SO question: Appearance proxy - setShadowImage alternative for iOS 5? for a code snippet
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.
I have a simple app with a nav bar controller and I would like to change its navigation bar while overriding the drawRect function. I read in many places here that all I need to do is just to paste the code below above my appDelegate. Sadly, it doesnt seem to do anything for me. I tried as a start to just have the color of the nav bar changed, before i try to add an image to it, but again, nothing happens. What am i missing here
I tired to insert this code above the AppDelegate:
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColor(context, CGColorGetComponents( [color CGColor]));
CGContextFillRect(context, rect);
}
#end
And with the background image:
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
// Drawing code
UIImage *img = [UIImage imageNamed: #"13.png"];
[img drawInRect:CGRectMake(0, 0, 320, self.frame.size.height)];
}
#end
Thanks for the help!
This code declares a category on UINavigationBar and redefines the behaviour of its drawRect: method. Try to put the code snippet (second one) at the end of the .m file of the app delegate.
Although the first should work, another more clean option is to write the category in a separate file you will include in the app delegate.
Also make sure the picture you are loading exists in the app bundle.
Ok - solution found! This doesnt seem to work on the iPhone 5.0 simulator!
I've subclassed an UIAlertView as follow:
#interface NarrationAlertView : UIAlertView {
UIImage * backgroundImage; //The image I want as custom background
UILabel * textualNarrationView; //The test I wanna be displayed on the view
}
And implemented it this way :
- (id)initNarrationViewWithImage:(UIImage *)image{
if (self = [super init]){
UILabel * alertTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.textualNarrationView = alertTextLabel;
[alertTextLabel release];
[self addSubview:alertTextLabel];
}
return self;
}
- (void)drawRect:(CGRect)rect {
/* Here I draw the image as background */
CGSize imageSize = self.backgroundImage.size;
[self.backgroundImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
}
- (void)layoutSubviews {
/* To fit the text */
[textualNarrationView sizeToFit];
CGRect textRect = textualNarrationView.frame;
textRect.origin.x = (CGRectGetWidth(self.bounds) - CGRectGetWidth(textRect)) / 2;
textRect.origin.y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(textRect)) / 2;
textRect.origin.y -= 70;
textualNarrationView.frame = textRect;
}
- (void)show{
/* Showing the view */
[super show];
CGSize imageSize = self.backgroundImage.size;
self.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
}
On the previous versions of iOS (I'm always testing on the simulator) the subclass run fine and when shown it displayed the custom background image drawn correctly and just text upon it, whereas in the 4.2 version it draws the UIAlertView classic background (the blue rounded corners rectangle) on top of my image.
What am I doing wrong? Any suggestion about UIAlertView programming and UIView too will be appreciated.
UPDATE Has anyone got some UIAlertView replacement class to share?
We had a similar problem with UIAlertView in iOS 4.2; we were customizing the layout, added text boxes and rearranging the buttons.
This won't be a popular answer, but due to the changes to UIAlertView, we had to abandon using it entirely for this purpose. We always knew it was a fragile implementation since customizing/subclassing UIAlertView isn't officially supported and makes assumptions about the internal structure of the view hierarchy, but the release of 4.2 really kicked us into gear.
In the end, we implemented our own UI element to replace the customized UIAlertView.
Prior to iOS 4.2 UIAlertView's standard dark blue rounded rectangle was drawn in drawRect. the rounded rectangle could be removed by subclassing UIAlertView and implementing drawRect without calling super. however in 4.2 the rounded rectangle is a UIImageView subview.
the quick, easy (not best) solution: If you are not adding any UIImageView instances to your UIAlertView subclass you can simply remove the default UIImageView that is loaded by observing subview additions:
- (void)didAddSubview:(UIView *)subview {
if ([subview isMemberOfClass:[UIImageView class]]) {
[subview removeFromSuperview];
}
}
I got burned by this too. I ended up writing a replacement class, which I'm sharing on github:
https://github.com/TomSwift/TSAlertView
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to add background image on iphone Navigation bar?
I am looking for a way to have a custom navigation bar and need to have a custom navigation bar background to achieve this. I was looking around for how to do this, but could not find a solution. If anyone has the solution, help is much appreciated.
Since iOS5, you can easily set a custom background, with the method setBackgroundImage:forBarMetrics:
But you must check if the user's phone has the right OS.
if ([self.navigationController.navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)])
{
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"texture.png"]
forBarMetrics:UIBarMetricsDefault];
}
This is a nicer solution, cause it's in the doc.
(supplemental to Andrew Johnson's response)
The linked Apple.com post includes 3 or 4 different solutions, most of which only "half" work. I think the most elegant/effective of them is this one:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"NavigationBar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
HOWEVER ... it's not good-practice ObjC to od that as a category (should be an override), and it has some problems of its own.
So, a more general and powerful solution is here:
http://samsoff.es/posts/customize-uikit-with-method-swizzling
You can just add a subview (a UIImageView) to the navaigationBar, which is just a UIView subclass.
UINavigationBar nb = [[UINavigationBar alloc]init];
[nb addSubview: foo];
[nb release];
Here's a forum post that describes how to wrap this up into a category: http://discussions.apple.com/thread.jspa?threadID=1649012&tstart=0
Copy this into viewDidLoad. It will check for iOS 5 and use the preferred method, otherwise it will add a subview to the navBar for iOS versions < 5.0. This will work provided that your custom background image has no transparencies.
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
NSLog(#"%f",version);
UIImage *backgroundImage = [UIImage imageNamed:#"myBackgroundImage.png"];
if (version >= 5.0) {
[self.navigationController.navigationBar setBackgroundImage:backgroundImage forBarMetrics:UIBarMetricsDefault];
}
else
{
[self.navigationController.navigationBar insertSubview:[[[UIImageView alloc] initWithImage:backgroundImage] autorelease] atIndex:1];
}