I have a UINavigationBar with a custom background and my own back button. I did the following to try achieve this.
//custom background
UIImageView *background = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"navigation-bar.png"]] autorelease];
[self.navigationController.navigationBar insertSubview:background atIndex:0];
//custom back button
UIImage *buttonImage = [UIImage imageNamed:#"btn_back.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:buttonImage forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[button addTarget:self action:#selector(backPressed:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = customBarItem;
[customBarItem release];
The back button always works, but it can only be seen if I don't apply my custom background. Could someone please point out how to achieve both?
Thanks in advance.
Ricky.
You can set background image of navigation bar by using this
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
if([self isMemberOfClass:[UINavigationBar class]])
{
UIImage *image;
image=[UIImage imageNamed:#"nav_bar_img"];
CGContextClip(ctx);
CGContextTranslateCTM(ctx, 0, image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx,
CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
}
else
{
[super drawLayer:layer inContext:ctx];
}
}
#end
I ended up doing the following.
#implementation UINavigationBar (Category)
- (void)drawRect:(CGRect)rect
{
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.navImage drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
Every time I want to change the background of the bar, I change the delegate's navImage property and call -setNeedsDisplay on my navigation bar.
Thanks for your help.
Related
As my title, I have went through several websites and failed to achieve the result. However, I managed to add an image on the navigation bar successfully. These are two problems I'm facing currently, both are under UINavigationBar section.
1. Unable to display the rightbarbutton
2. Unable to hide the back arrow "<"
MasterViewController.m
//Back Button
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *backBtnImage = [UIImage imageNamed:#"sidemenu.png"];
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
backBtn.frame = CGRectMake(0, 0, 54, 30);
//Rightbarbutton
UIButton *infoButton = (UIButton *) [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon_info.png"]];
UIBarButtonItem *infoButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
self.navigationItem.rightBarButtonItem = infoButtonItem;
//Image (Working and displaying)
UIImage *image = [UIImage imageNamed: #"icon_image.png"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(245, 0, 30, 42)];
[imageView setImage:image];
[self.navigationController.navigationBar addSubview:imageView];
I think you should initialize you rightItem's content as an UIButton and not a cast from ImageView to UIButton... so the same procedure you are doing for the back item..
This is your UINavigationItem+AtAdditions.h file
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, ATNavigationItemButtonSide) {
ATNavigationItemButtonSideRight,
ATNavigationItemButtonSideLeft
};
#interface UINavigationItem(ATAdditions)
// This method adds an image-only button to the requested size (right or left) of the current navigation item, the image can't be tapped
- (void)addColorfulImage:(NSString*)imageName toSide:(ATNavigationItemButtonSide)side;
#end
This is your UINavigationItem+AtAdditions.m file
#import "UINavigationItem+ATAdditions.h"
#implementation UINavigationItem(ATAdditions)
- (void)addColorfulImage:(NSString*)imageName toSide:(ATNavigationItemButtonSide)side {
// Create a button with the required image on it
UIImage *image = [UIImage imageNamed:imageName];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.bounds = CGRectMake(0, 0, image.size.width, image.size.height );
[button setImage:image forState:UIControlStateNormal];
// Stop the events (it's an image only)
button.userInteractionEnabled = NO;
// Add the button to the navigation item
if (side == ATNavigationItemButtonSideLeft) {
self.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
}
else if (side == ATNavigationItemButtonSideRight) {
self.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
}
}
#end
Later you can import the .h file and use it on the navigation item in your view controller.
Here is my code to draw my square in my class shape.h (Subclass UIView)
- (void)drawRect:(CGRect)rect
{
//Square
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context,
[UIColor blueColor].CGColor);
CGRect rectangle = CGRectMake(20,20,80,80);
CGContextAddRect(context, rectangle);
CGContextStrokePath(context);
}
What i need help with is taking my code and implementing it into a button in my ViewController(h). Saying that whenever i touch the button i would like my square to appear. Im not sure if i'm explaining my self correctly, correct me if im wrong.
I would do it another way:
This is you IBAction that is called, when the button is pressed:
-(IBAction)buttonPressed:(id)sender
{
UIView *square = [[UIView alloc] init];
square.frame = CGRectMake(20, 20, 80, 80);
square.backgroundColor = [UIColor blueColor];
[self.view addSubview:square];
}
This is the IBAction for a circle:
-(IBAction)buttonPressed:(id)sender
{
UIView *circle = [[UIView alloc] init];
circle.frame = CGRectMake(20, 20, 80, 80);
circle.backgroundColor = [UIColor blueColor];
circle.layer.cornerRadius = circle.frame.size.height/2;
[self.view circle];
}
Is that what you wanted? You don't need an extra subclass of UIView for this.
To connect the IBAction to the UIButton, right click in interface builder from the button to this method in code or like this:
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
If you like to draw something more complex (like a triangle) you can subclass UIView. The Code above was for some really easy things. Here is some sample code for a triangle but I think you get the point:
ViewController.m:
#import "ViewController.h"
#import "triangle.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
[button setTitle:#"Press me" forState:UIControlStateNormal];
button.backgroundColor = [UIColor blackColor];
button.titleLabel.textColor = [UIColor whiteColor];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(IBAction)buttonPressed:(id)sender
{
triangle *view = [[triangle alloc] initWithFrame:CGRectMake(0, 80, 200, 200)];
view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:view];
}
#end
triangle.m:
#import "triangle.h"
#implementation triangle
-(void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextBeginPath(ctx);
CGContextMoveToPoint (ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); // top left
CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMidY(rect)); // mid right
CGContextAddLineToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); // bottom left
CGContextClosePath(ctx);
CGContextSetRGBFillColor(ctx, 100/255.f, 100/255.f, 100/255.f, 1);
CGContextFillPath(ctx);
}
#end
I'm experiencing excessive UIBarButtonItem padding/spacing when using the LeftBarItems and RightBarItems (see image below). The icons used on the UIBarButtonItems do not contain extra padding. So I would like to know what's causing this?
I use this in order to remove space before the first item.
However it doesn't work between system items like UIBarButtonSystemItemAdd, only with UIBarButtonItem that has an image.
#interface UIBarButtonItem (NegativeSpacer)
+(UIBarButtonItem*)negativeSpacerWithWidth:(NSInteger)width;
#end
#implementation UIBarButtonItem (NegativeSpacer)
+(UIBarButtonItem*)negativeSpacerWithWidth:(NSInteger)width {
UIBarButtonItem *item = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil
action:nil];
item.width = (width >= 0 ? -width : width);
return item;
}
#end
Use it like this:
UIBarButtonItem *item0 = [UIBarButtonItem negativeSpacerWithWidth:13];
UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"sidebar.png"]
style:UIBarButtonItemStylePlain
target:vc
action:#selector(sideMenuAction:)];
NSArray* items = #[item0, item1];
[vc.navigationItem setLeftBarButtonItems:items animated:NO];
[vc.navigationItem setLeftItemsSupplementBackButton:YES];
You can move the image
self.myBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 25, 0, -25);
Apple silently increased the horizontal spacing constraints for UIBarButtonItems and sadly, still hasn't added any UIAppearance methods to adjust the horizontal positioning of UIBarButtonItems.
The best solution (which worked for me) is to wrap your UIBarButtonItems in a UIView using initWithCustomView: and adjust the bounds of that custom view to get your desired positioning. Here's a good answer on how to do this.
If you want to take things a step further, you can create a category on UIBarButtonItem with class methods that return the bar buttons you use throughout your app. That way, when you a need a bar button, you can call something like:
self.navigationItem.leftBarButtonItem = [UIBarButtonItem mySearchBarButtonItemWithTarget:self selector:#selector(search)];
There are two kinds of button on the navigation bar in iOS 7: button with image and button with text. I wrote a class to do it. Here is how:
GlobalUICommon.h:
#interface UIBarButtonItem(CustomUIOfONE)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image highlightedImage:(UIImage*)highlightedImage xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action;
+ (UIBarButtonItem*)barItemWithTitle:(NSString*)title xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action;
#end
GlobalUICommon.m:
#implementation UIBarButtonItem(CustomUIOfONE)
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image highlightedImage:(UIImage*)highlightedImage xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[button setImage:image forState:UIControlStateNormal];
[button setImage:highlightedImage forState:UIControlStateHighlighted];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[button setImageEdgeInsets:UIEdgeInsetsMake(0, xOffset, 0, -xOffset)];
}
UIBarButtonItem *customUIBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
return customUIBarButtonItem;
}
+ (UIBarButtonItem*)barItemWithTitle:(NSString*)title xOffset:(NSInteger)xOffset target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
[button.titleLabel setFont:[UIFont systemFontOfSize:15]];
[button setFrame:CGRectMake(0, 0, [button.titleLabel.text sizeWithFont:button.titleLabel.font].width + 3, 24)];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[button setContentEdgeInsets:UIEdgeInsetsMake(0, xOffset, 0, -xOffset)];
}
UIBarButtonItem *customUIBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
return customUIBarButtonItem;
}
#end
YourViewController.m:
Example for Button with Image:
UIBarButtonItem* leftButtomItem = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:#"yourImage"]
highlightedImage:[UIImage imageNamed:#"yourImage"]
xOffset:-11
target:self
action:#selector(yourHandler)];
self.navigationItem.leftBarButtonItem = leftButtomItem;
UIBarButtonItem* rightButtonItem = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:#"yourImage"]
highlightedImage:[UIImage imageNamed:#"yourImage"]
xOffset:11
target:self
action:#selector(yourHandler)];
self.navigationItem.rightBarButtonItem = rightButtonItem;
Example for Button with Text:
self.navigationItem.leftBarButtonItem = [UIBarButtonItem barItemWithTitle:#"yourText" xOffset:-11 target:self action:#selector(yourHandler:)];
self.navigationItem.rightBarButtonItem = [UIBarButtonItem barItemWithTitle:#"yourText" xOffset:11 target:self action:#selector(yourHandler:)];
That's it.
I solved this by using storybord interface.
1.Select the Bar item.
2.Select the Size Inspector.
Here you can find image Inset,using top,bottom AND left , right you can change the position of Bar Item.
As #Luda comments, the solution is to
self.myBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 25, 0, -25);
However, #andrrs also points a problem here when inset is large: the hit area. In this case,we have to implement a way to setHitTestEdgeInsets. Below is an category method:
#implementation UIButton (Extensions)
#dynamic hitTestEdgeInsets;
static const NSString *KEY_HIT_TEST_EDGE_INSETS = #"HitTestEdgeInsets";
-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:#encode(UIEdgeInsets)];
objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS);
if(value) {
UIEdgeInsets edgeInsets; [value getValue:&edgeInsets]; return edgeInsets;
}else {
return UIEdgeInsetsZero;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
return [super pointInside:point withEvent:event];
}
CGRect relativeFrame = self.bounds;
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
#end
Is there a way to customize ALL uibarbuttonitems to have a background image without using UIApperance? Unfortunately our codebase has some limitations and we're not able to use UIAppearance so I'm looking for an alternative.
Thanks in advance.
Edit: I was able to do this by subclassing UIBarButtonItem and it works for most of my buttons...but I'm wondering specifically how to do it with default buttons like UITableView edit buttons and UINavigationController backbuttons.
Thanks!
You also can make subclass like this. First i go to make subclass of UIBarButtonItem. My CustomBarButtonItem.h should be like this.
#interface CustomBarButtonItem : UIBarButtonItem
+ (CustomBarButtonItem *)sharedInstance;
- (UIBarButtonItem *)backButtonwithTarget:(id)target andAction:(SEL)action;
#end
then inside my CustomBarButtonItem.m should like this.
#implementation CustomBarButtonItem
static CustomBarButtonItem * _sharedInstance = nil;
+ (CustomBarButtonItem *)sharedInstance
{
if (_sharedInstance != nil)
{
return _sharedInstance;
}
_sharedInstance = [[CustomBarButtonItem alloc] init];
return _sharedInstance;
}
- (UIBarButtonItem *)backButtonwithTarget:(id)target andAction:(SEL)action
{
UIImage *backImage = [UIImage imageNamed:#"back_btn_normal.png"];
UIImage *backPressed = [UIImage imageNamed:#"back_btn_hilight.png"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[backButton setBackgroundImage:backImage forState:UIControlStateNormal];
[backButton setBackgroundImage:backPressed forState:UIControlStateHighlighted];
const CGFloat BarButtonOffset = 5.0f;
[backButton setFrame:CGRectMake(BarButtonOffset, 0, backImage.size.width, backImage.size.height)];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, backImage.size.width, backImage.size.height)];
[containerView addSubview:backButton];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:containerView];
return item;
}
#end
Then where ever you want to make custom NavigationItem (Button in the NavigationBar) you can put the code below for me it custom.
- (void)viewDidLoad
{
self.navigationItem.leftBarButtonItem = [[CustomBarButtonItem sharedInstance] backButtonwithTarget:self andAction:#selector(searchButtonTapped:)];
}
All of my code are working fine. Hopefully it will help you.
you can do something like this. just copied it out of my project.
_leftBarButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[self imageButton]]
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showLeftViewController)];
[navController.navigationBar.topItem setLeftBarButtonItem:_leftBarButton];
To do this: create category on UINavigationBar and override method drawRect
#implementation UINavigationBar (CustomBackground)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"background"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
This code allows you to change background for all UINavigationBar in a project.
I'm just adding a quick intro screen with a button, which compiles and displays properly, but my button doesn't respond.
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"viewDidLoad!");
[self showIntro];
}
// make a quick Intro screen
- (void) showIntro {
UIImageView *myBackImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
[myBackImage setImage:(UIImage *) [UIImage imageNamed:#"myBkgnd.png"]];
UIButton *clickBtn = [UIButton buttonWithType:UIButtonTypeCustom ];
[clickBtn addTarget:self action:#selector(myButton_pressed:) forControlEvents:UIControlEventTouchUpInside];
[clickBtn setImage:(UIImage *) [UIImage imageNamed:#"btnImage.png"] forState:UIControlStateNormal];
clickBtn.frame = CGRectMake(111, 200, 75, 60);
[myBackImage addSubview:clickBtn];
[self.view addSubview:myBackImage];
}
- (void)myButton_pressed:(UIButton*)button {
NSLog(#"Button Pressed!");
}
What am I overlooking?
Thanks!
As you have added UIButton in UIImageView but didn't you forgot imageView's userInteraction by default NO. So :
myBackImage.userInteractionEnabled = YES;
EDIT : Just formatted