So I've been researching on how to have an image in UINavigationBar and the safest way to do it that works for iOS 4 and iOS 5 is via subclassing the UINavigationBar, so I had:
#interface CustomNavigationBar : UINavigationBar
#end
#implementation CustomNavigationBar
-(void) drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"myNavBarImage"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
Now say I wanted to hook up ProfileViewController to have this custom UINavigationBar, how do I do that? Here's how I set ProfileViewController.
ProfileViewController *profile = [[ProfileViewController alloc] init];
localNavigationController = [[UINavigationController alloc] initWithRootViewController:profile];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[profile release];
Any idea on how to wire this up? I've read that this has to be done via IB by changing the class of my UINavigationBar to the class that I just created. However, I don't have any UINavigationBar in my xib file, as it is created programatically via the UINavigationController.
One other issue also is that if I wanted to have a dynamic image that I can change/specify to the UINavigationBar, how do I do this? As of now it is hard wired that the UINavigationbar will have an image called myNavBarImage, but what if I wanted to set it to something else so I don't have to create 10 custom UINavigationBar, I somehow wanted to set the image name.
There is a solution similar to yours but uses categories instead of subclassing:
http://www.developers-life.com/custom-uinavigationbar-with-image-and-back-button.html
However, you have other choices that will make it easier for you such as adding an image as a subview to UINavigationBar. It is a safe way to do it,and I used it in a previous project which was approved.
My favorite way to do it is hiding the navigation bar and adding an imageView with buttons to have full control on how the navigation bar looks like (I especially use it when I need to add custom buttons as well).
Edit (Code to use for the second issue):
At viewDidLoad
UIImageView *imageview = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"image1.png"]];
[self.navigationController.navigationBar addSubview:imageview];
[self.navigationController.navigationBar sendSubviewToBack:imageview];
[imageview release];
At the method that changes the image:
-(void) changeNavigationBarImage: (NSString *) imageName{
for (UIView *view in self.navigationController.navigationBar.subviews) {
if ([view isKindOfClass:[UIImageView class]])
{
[(UIImageView *)view setImage:[UIImage imageNamed:imageName]];
break;
}
}
}
You can call this method when you want to change the image (i.e. button or timer)
Related
I want to have a UITabBarController, which is has an alpha of 0.5, whose transparency allows you to see a view in the background. The background view has to be accesible and changeable.
I'm able to add the background using this technique: https://gist.github.com/1157542
It's a Category which adds a subview to the UITabBarController, and sends the subview to the back. However, because it's a category, I can't make the subview a property. So I can't really access it.
Is there a way to make this background view more flexible and accessible? So I could, for instance, add other subviews to it easily from any of the tab bar controller's view controllers?
Instead of a category, you should subclass UITabBarController. This will allow you to have finer control over the object. Here's an example of a subclass.
// MPCustomTabBar.h
#interface MPCustomTabBar : UITabBarController
- (void) setBackgroundImage:(UIImage *)image;
#end
// MPCustomTabBar.m
#interface MPCustomTabBar
- (void) setBackgroundImage:(UIImage *)image {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,480)];
imageView.backgroundColor = [UIColor colorWithPatternImage:i];
[[self view] addSubview:imageView];
[[self view] sendSubviewToBack:imageView];
[[self view] setOpaque:NO];
[[self view] setBackgroundColor:[UIColor clearColor]];
[imageView release];
}
#end
Now you can do all the customization you want, alloc and init your new subclass by something like this:
MPCustomTabBar *bar = [[MPCustomTabBar alloc] init];
A solution to my problem might be simply this..
In my AppDelegate, just before [self.window makeKeyAndVisible];
self.theImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image1.png"]];
[self.tabBarController.view insertSubview:self.theImage atIndex:0];
I can then easily change this image, in any of the tab bar controller's view controllers, like:
AppDelegate *appDelegate= (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.theImage.image = [UIImage imageNamed:#"image2.png"];
.. no categories required.
Here's what worked for me:
Make TabBar transparent (either by Storyboard or programmatically) by setting the Tint- and Background to Clear Color and Opaque to No.
The black background color is actually the window's colour. Assign the image you want to use as the background to the window itself:
UIImage *i = [UIImage imageNamed:#"main_background.png"];
UIColor *c = [[UIColor alloc] initWithPatternImage:i];
[self.window setBackgroundColor:c];
Many of you have probably noticed that we cant anymore create a category to override UINavigationBar drawRect in iOS5. Now actually I don't see a good way of implementing this customization without using a subclass of UINavigationBar and set it inside a navigationController->navigationBar in my MainWindow.xib. However, TTNavigator do not recognize that navigationController since every TTViewController has their own superclass of TTNavigationController.
I don't know how to solve it for Three20 but in general for supporting IOS 5 you can use this method
UIImage *image = [UIImage imageNamed:#"header.png"];
if([navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)] ) {
//iOS 5 new UINavigationBar custom background
[navigationBar setBackgroundImage:image forBarMetrics: UIBarMetricsDefault];
}
else{
UIImageView *imgView = [[[UIImageView alloc] initWithImage:image] autorelease];
[imgView setUserInteractionEnabled:NO];
[imgView setTag:TOOLBAR_TAG];
[navigationBar insertSubview:imgView atIndex:0];
}
I would like to set an image background to the navigation bar on my iphone app. Most solutions suggest using drawRect in a category like:
#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, Apple does not recommend this. Any other suggestion?
Thx for helping,
Stephane
Apple strongly advise us to use subclasses rather than categories (WWDC 2011, Session 123).
Create a subclass which implements the drawRect: method and set the class of your navigation bar to your custom class:
if you're working in Interface Builder, change the class in the inspector
if you create a stand-alone navigation bar (without nav controller), instantiate your custom class
if you create a navigation controller programmatically, you could take advantage of the ObjC runtime.
Class switch at runtime:
#import <objc/runtime.h>
...
object_setClass(theNavController.navigationBar, [CustomNavigationBar class]);
You should also avoid using [UIImage imageNamed:...] each time in drawRect: as it might have an impact on performance (for animations). Cache it in an ivar:
if (!bgImage) {
bgImage = [[UIImage imageNamed:#"NavigationBar.png"] retain];
}
[bgImage drawInRect:...];
(and release it in dealloc)
Note: As iOS 5 is still under NDA, I can't mention how you could easily add a background image. Check out the docs for UINavigationBar.
Tested Code : 100 % works
in ur ViewDidLoad
UIImageView *iv=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"urNavBarImage.png"]];
self.navigationItem.titleView = iv;
[iv release];
NOTE:urNavBarImage should be exact size of Navigation Bar. Like this u can change every Viewcontroller Navigation bar.
I have created a custom category for UINavigationBar as follows
UINavigationBar+CustomImage.h
#import <UIKit/UIKit.h>
#interface UINavigationBar (CustomImage)
- (void) setBackgroundImage:(UIImage*)image;
- (void) clearBackgroundImage;
- (void) removeIfImage:(id)sender;
#end
UINavigationBar+CustomImage.m
#import "UINavigationBar+CustomImage.h"
#implementation UINavigationBar (CustomImage)
- (void) setBackgroundImage:(UIImage*)image {
if (image == NULL) return;
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(110,5,100,30);
[self addSubview:imageView];
[imageView release];
}
- (void) clearBackgroundImage {
NSArray *subviews = [self subviews];
for (int i=0; i<[subviews count]; i++) {
if ([[subviews objectAtIndex:i] isMemberOfClass:[UIImageView class]]) {
[[subviews objectAtIndex:i] removeFromSuperview];
}
}
}
#end
I invoke it from my UINavigationController
[[navController navigationBar] performSelectorInBackground:#selector(setBackgroundImage:) withObject:image];
I have customize my UINavigationBar with UINavigationBar drawRect and it is working fine until I fire up MFMailComposeViewController, which gives me a trouble as I couldn't override UIBarButton for the mail class, it create akward view like below:
While I try to prevent the MFMailComposeViewController and produce code below for using the default drawRect for MFMailComposeViewController, it even worse it create a black UIBar:
The Code:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
//Prevent Mail Controller For Customizing NavigationBar
for (UIView* next = [self superview]; next; next = next.superview) {
UIResponder* nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[MFMailComposeViewController class]]) {
[super drawRect:rect];
return;
}
}
UIImage *image = [UIImage imageNamed: #"titleBar.png"];
[image drawInRect:CGRectMake(0, 0, 320, 44)];
}
#end
What I am trying to do here is to make sure MFMailComposeViewController having the same style of UINavigationBar and UIButtonItem. It can be either way:
1) Both UINavigationBar and UIButtonItem have customized background
2) Default UINavigationBar style (gradient blue)
Would like to know can I achieve this? Thanks in advance.
After some hacking and testing, still not manage to customize the button. But this is the closest I can get, by setting the tint color of mail controller.
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
mailController.mailComposeDelegate = self;
mailController.navigationBar.tintColor = [UIColor brownColor];
I am having problems with properly displaying background image of navigation view.
Here is the pic:
Here is the code:
- (id)initWithStyle:(UITableViewStyle)style {
if (self = [super initWithStyle:style]) {
UIImage *image = [UIImage imageNamed: #"bg_table_active.png"];
UIImageView *imageview = [[UIImageView alloc] initWithImage: image];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithTitle:NSLocalizedString(#"Settings", #"")
style:UIBarButtonItemStyleDone
target:self
action:#selector(GoToSettings)];
self.navigationItem.titleView = imageview;
self.navigationItem.rightBarButtonItem = addButton;
self.navigationItem.hidesBackButton = TRUE;
}
return self;
}
How can I make the picture stretch to the whole navigation view?
I do exactly this in my app. Within AppDelegate I have this code:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"custom_nav_bar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
I modified Mike Rundle's version so that the a custom image can be set if necessary. I also merged in 40lb-suit-of-bees suggested changes. initImageDictionary needs to be called during initialisation:
//UINavigationBar+CustomImage.h
#import <Foundation/Foundation.h>
#interface UINavigationBar(CustomImage)
+ (void) initImageDictionary;
- (void) drawRect:(CGRect)rect;
- (void) setImage:(UIImage*)image;
#end
//UINavigationBar+CustomImage.m
#import "UINavigationBar+CustomImage.h"
//Global dictionary for recording background image
static NSMutableDictionary *navigationBarImages = NULL;
#implementation UINavigationBar(CustomImage)
//Overrider to draw a custom image
+ (void)initImageDictionary
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
}
- (void)drawRect:(CGRect)rect
{
NSString *imageName=[navigationBarImages objectForKey:[NSValue valueWithNonretainedObject: self]];
if (imageName==nil) {
imageName=#"header_bg.png";
}
UIImage *image = [UIImage imageNamed: imageName];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
//Allow the setting of an image for the navigation bar
- (void)setImage:(UIImage*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject: self]];
}
#end
Mike Rundle and Casebash's code is great. I used [NSValue valueWithNonretainedObject:self] to avoid the copyWithZone error. Wrapping self in an NSValue object allows it to be copied into the navigationBarImages dictionary.
- (void)drawRect:(CGRect)rect
{
NSString *imageName=[navigationBarImages objectForKey:[NSValue valueWithNonretainedObject:self]];
...}
- (void)setImage:(NSString*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject:self]];
}
http://foobarpig.com/iphone/uinavigationbar-with-solid-color-or-image-background.html
Hope it helps..
You can use this also
if([self.navigationController.navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)] ) {
//iOS 5 new UINavigationBar custom background
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"navbg_ForiPhone5_Imagename.png"] forBarMetrics: UIBarMetricsDefault];
} else {
[self.navigationController.navigationBar insertSubview:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"navbg_ForOtherIphone_Imagename.png"]] atIndex:0];
}
`
http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/UsingNavigationControllers/UsingNavigationControllers.html#//apple_ref/doc/uid/TP40007457-CH7
Looking at Figure 1 in that link - would it be better to set the backgroundImage on your navigationbar not your navigationitem?
UIImage *image = [UIImage imageNamed: #"navigator.png"];
[_homeNavigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
Unfortunately, there is no support for using custom background images in a navigation bar in iPhone OS 3.0 or any previous versions. The only way to customize the appearance is to set the style and tint color. Not perfect, I know.
In your code you are trying to stretch the title view of the navigation bar to "go under" the right button. But this is impossible since the three views of a navigation bar (back button, title, and right button) are supposed to be in the same layer and are adjusted to not overlap. This is a feature.
I know there are a number of third-party apps that change the background image but they are "hacking" the system and are using unsupported private API:s or assumptions of the internal data structures of the navigation bar. These programs will most likely fail (crash or display incorrectly) in future versions of iPhone OS.
You most likely don't want to mess with this. Accept the fact that that you cannot (yet) have a custom background image in navigation bars. It hurts, I know. But if you hack the system and your app fails in a future versions of the OS, Apple will pull the app from the app store and you will lose all revenue until you have changed the app. It's your call...