I create left Bar button item with next method:
- (void)leftButtonOnNavigationItem:(NSString *)imageName type:(NSInteger)type
{
UIImage *backImg = [UIImage imageNamed:imageName];
UIButton *backBtn = [[UIButton alloc] initWithFrame:CGRectMake(0.f, 0.f, backImg.size.width, backImg.size.height)];
if (type == 0)
[backBtn addTarget:self.navigationController action:#selector(pop) forControlEvents:UIControlEventTouchUpInside];
else
[backBtn addTarget:self action:#selector(leftMake) forControlEvents:UIControlEventTouchUpInside];
backBtn.contentMode = UIViewContentModeCenter;
[backBtn setImage:backImg forState:UIControlStateNormal];
UIView *backBtnView = [[UIView alloc] initWithFrame:backBtn.bounds];
backBtnView.bounds = CGRectOffset(backBtnView.bounds, -6, 0);
[backBtnView addSubview:backBtn];
UIBarButtonItem *backBarBtn = [[UIBarButtonItem alloc] initWithCustomView:backBtnView];
self.navigationItem.leftBarButtonItem = backBarBtn;
}
I need add button on View because when I use CGRectOffset for button it is not work. So with this case my button on one od the UIViewController shown
So it is what I want. But UIBarButton began clicked only on UIBarButton (area of the rectangle). But when I add only button in UIBarButtonItem it is work even if I clicked on area of UINavigationBar that you can see on screen.
I add button like this:
- (void)leftButtonOnNavigationItem:(NSString *)imageName type:(NSInteger)type
{
UIImage *backImg = [UIImage imageNamed:imageName];
UIButton *backBtn = [[UIButton alloc] initWithFrame:CGRectMake(0.f, 0.f, backImg.size.width, backImg.size.height)];
if (type == 0)
[backBtn addTarget:self.navigationController action:#selector(pop) forControlEvents:UIControlEventTouchUpInside];
else
[backBtn addTarget:self action:#selector(leftMake) forControlEvents:UIControlEventTouchUpInside];
backBtn.contentMode = UIViewContentModeCenter;
[backBtn setImage:backImg forState:UIControlStateNormal];
// UIView *backBtnView = [[UIView alloc] initWithFrame:backBtn.bounds];
// backBtnView.bounds = CGRectOffset(backBtnView.bounds, -6, 0);
// [backBtnView addSubview:backBtn];
UIBarButtonItem *backBarBtn = [[UIBarButtonItem alloc] initWithCustomView:backBtn];//View];
self.navigationItem.leftBarButtonItem = backBarBtn;
}
and now my button shown like this
So CGRectOffset not work and my UIBarButton very close to left side of screen, but it is work fine by tap all area on navigation bar, that you see on screen. So I can touch in all area on navigation Bar and UIBarButton will work fine
So I need: UIBarButton must shown like in first case with more area from left side. But it must work by click not only button but all surround ares like in second case. What I must do?
EDIT
In first case button clicked if i tap next area
In second case button clicked if i tap next area
I need position of button like in first case (more origin.x from left side) but clickable area like in second case
All controls/elements that can be interacted with inherit from UIView. UIView provides a method called - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
It is possible to reimplement this method in your own subclass to increase or decrease the valid hit region of the view (or button in your case). For example, to grow the hit region by 5 points in all directions
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return CGRectContainsPoint( CGRectInset( self.bounds, -5, -5 ), point );
}
Note the negative values when using CGRectInset(). This is because the function is designed for shrinking a CGRect, so by specifying a negative value, the function will expand the CGRect.
Hope this helps!
You can use UIEdgeInsetsMake. It work for me.
UIImage *buttnImage=[UIImage imageNamed:imageName];
UIButton *backButton=[UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:buttnImage forState:UIControlStateNormal];
backButton.frame=CGRectMake(0, 0, (buttnImage.size.width+leftMargin+rightMargin), (buttnImage.size.height+topMargin+bottomMargin));
[backButton setImageEdgeInsets:UIEdgeInsetsMake(topMargin, leftMargin, bottomMargin, rightMargin)];
UIBarButtonItem *backBtn=[[UIBarButtonItem alloc]initWithCustomView:backButton];
self.navigationItem.leftBarButtonItems=#[backBtn];
A view's frame is the size and position of the view within a superview (its parent). So when you go to set backBtnView initWithFrame try:
UIView *backBtnView = [[UIView alloc]
initWithFrame:CGRectOffset(backBtn.frame, -6.0, 0.0)];
and forget about bounds all together, I think that will put you in the right direction.
I fixed it in used accessibility frame.
Just set accessibility frame for button with increased height and width and it will work.
Related
I have a little problem using the navigation bar in an iOS project using objective-c.
I have some view controllers which are managed by a navigationviewcontroller, in a hierarchy like 1-2-3.
For the Viewcontrollers 2 and 3, I define custom back buttons in the viewWillAppear function, as I need to assign them a more complex logic when touching them. This is the reason why I do not set the back buttons in the previous view controller.
Everything works fine like this, my single problem is that, on transition from one view to another, the arrow of the back button is overlayed by a little view in the color of the navigation bar's background color until up to 50%, and then disappears when the transition has finished.
I create my back buttons in the navigationbar with this function, which is called in viewWillAppear:
- (void)setupCustomBackButton:(NSString *)title
action:(SEL)action
buttonFrame:(CGRect)buttonFrame {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
backButton.frame = buttonFrame;
[backButton setImage:[UIImage imageNamed:#"backArrowOwn"] forState:UIControlStateNormal];
backButton.imageEdgeInsets = UIEdgeInsetsMake(0.0f, -8.0f, 0.0f, 0.0f);
[backButton setTitle:title forState:UIControlStateNormal];
[backButton.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
backButton.titleEdgeInsets = UIEdgeInsetsMake(0.0f, -3.0f, 0.0f, 0.0f);
[backButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[backButton addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = barButton;
}
I hope that maybe someone has a good hint for me! Thanks!
After some long trying, I finally managed to save the problem on my own.
So basically, when I create a leftBarButton, the button does not begin at the left margin, but has some space to the left margin. When I now inset my button image with a negative inset, the image will be basically outside my button frame. This portion is then weirdly overlayed on transition.
To fix this, I just add another UIBarButtonItem additionally to my leftBarButtonItems. This seems to fix the issue for me, although I do not really know why exactly.
Also, I think that it is weird that the leftBarButton in the navigationBar does not begin exactly at the margin, but has a space to the margin and I have to inset my image in order to get the exact same position for the image, as the default back button has.
My new code for creating a custom back button now looks like this:
- (void)setupCustomBackButton:(NSString *)title
action:(SEL)action
buttonFrame:(CGRect)buttonFrame {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
backButton.frame = buttonFrame;
[backButton setImage:[UIImage imageNamed:#"customBackArrow"] forState:UIControlStateNormal];
backButton.imageEdgeInsets = UIEdgeInsetsMake(0.0f, -8.0f, 0.0f, 0.0f);
[backButton setTitle:title forState:UIControlStateNormal];
[backButton.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
backButton.titleEdgeInsets = UIEdgeInsetsMake(0.0f, -3.0f, 0.0f, 0.0f);
[backButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[backButton addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
self.navigationItem.leftBarButtonItem = nil;
[self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects:negativeSpacer,barButton, nil] animated:NO];
}
Thanks everyone and I hope that my answer helps anyone else too!
Try to move the custom button initialization to the viewDidLoad method.
I'm using Xcode 8.0.
I add a custom left barbutton to my NavBar:
UIImage * imageNormal = [UIImage imageNamed:#"InfoIcon"];
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:#selector(testPressed ) forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundImage:imageNormal forState:UIControlStateNormal];
// set the frame of the button
button.frame = CGRectMake(0, 0, imageNormal.size.width, imageNormal.size.height);
UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, imageNormal.size.width, imageNormal.size.height)];
[view addSubview:button];
// set the barbuttonitem to be the view
UIBarButtonItem * barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
self.navigationItem.leftBarButtonItem = barButtonItem;
Here what I got:
unfortunately my button is not centered because the Nav bar has a custom image higher than the standard. But if I press the info Icon it works.
If I change the icon position with this line of code:
button.frame = CGRectMake(0, -30, imageNormal.size.width, imageNormal.size.height);
I got the right positioning:
But in this case the bar button is not anymore clickable, and I don't get why this is happening.
As workaround I attach a gesture to the view:
UITapGestureRecognizer *_tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self.revealViewController action:#selector(revealToggle:)];
[view addGestureRecognizer:_tapOnVideoRecognizer];
it works, but the tap is on the view and not on the button so that the changing status of the button when pressed is not working.
#JakubKnejzlik from https://stackoverflow.com/a/12554715/1320479
suggests:
This can be fixed by subclassing the wrapper view (superview of button, custom view of uibarbuttonitem) and overriding the hittest method to return button accordingly.
But I have no idea how to do it
Actually i want to place image and label on the toolbar as a UIBarButtonItem and provide a clickable effect to that Button.
So what i have done here is I have created one custom view and placed Image and Label on the same custom view and finally placed this custom view as UIBarButtonItem CustomView.
But when i set target and action for the same UIBarButtonItem, it is not calling the selector method.
The entire code is below.
Can anybody suggest me what's the mistake in my code ? and is there any other approach to achieve the same ????
Early suggestions would be much appreciated.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setToolbarHidden:NO];
UIView *customView = [[UIView alloc]
initWithFrame:CGRectMake(0,0,self.navigationController.toolbar.frame.size.width,self.navigationController.toolbar.frame.size.height)];
customView.backgroundColor = [UIColor colorWithRed:62.0/255.0 green:187.0/255.0 blue:150.0/255.0 alpha:1.0];
[self.navigationController.toolbar addSubview:customView];
UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(60,0,44,44)];
imgView.image = [UIImage imageNamed:#"documentImage.png"];
[customView addSubview:imgView];
UILabel *lbl = [[UILabel alloc]initWithFrame:CGRectMake(104,0,145,44)];
lbl.text = #"Scan Document";
lbl.textAlignment = NSTextAlignmentLeft;
[customView addSubview:lbl];
UIBarButtonItem *bar = [[UIBarButtonItem alloc]initWithCustomView:customView];
bar.target = self;
bar.action = #selector(scanDocument);
self.toolbarItems = [NSArray arrayWithObjects:bar, nil];
}
If you want to create button right hand side here is the code. i have already implemented it and working fine.
//Button Right side
UIImage *imgFavRest = [UIImage imageNamed:#"documentImage.png"];
UIButton *cart = [UIButton buttonWithType:UIButtonTypeCustom];
[cart setImage:imgFavRest forState:UIControlStateNormal];
cart.frame = CGRectMake(0, 0, imgFavRest.size.width, imgFavRest.size.height);
[cart addTarget:self action:#selector(showCart) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *rightBtn = [[UIBarButtonItem alloc] initWithCustomView:cart];
self.navigationItem.rightBarButtonItem = rightBtn;
Then use method like this . that's all
-(void)cartItemCount{
// Method
}
float textwidth = 50; // according to your text
UIImage *image = [UIImage imageNamed:#"logout"];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.bounds = CGRectMake( 10, 0, image.size.width+textwidth, image.size.height );
[btn addTarget:self action:#selector(scanDocument) forControlEvents:UIControlEventTouchUpInside];
[btn setImage:image forState:UIControlStateNormal];
[btn setTitle:#"test" forState:UIControlStateNormal];
[btn setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 5.0, 0.0, 0.0)];
//Adjust the coordinates and size of your button as your requirement.This is a sample code to guide you.
//PS:Take a UIButton in the nib file>Make it a custom button>Connect the IBOutlet to your custom button in the nib file.Thats it.
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithCustomView:btn];
self.navigationItem.rightBarButtonItem = rightButton;
I see very complicated answers, all of them using code. However, if you are using Interface Builder, there is a very easy way to do this:
Select the button and set a title and an image. Note that if you set
the background instead of the image then the image will be resized if
it is smaller than the button.IB basic image and title
Set the position of both items by changing the edge and insets. You
could even control the alignment of both in the Control section.
IB position set IB Control set
You could even use the same approach by code, without creating UILabels and UIImages inside as other solutions proposed. Always Keep It Simple!
EDIT: Attached a small example having the 3 things set (title, image and background) with correct insets Button preview
Note:: UIBarButtonItem inherits from UIBarItem and NSObject so it doesn't know anything about touches. It would be nice if the docs mentioned that the action and target properties only apply if the custom view is a UIButton.
I've created custom back bar button. And I added an UIview because I have to change position of it within bar.
UIImage *backButtonImage = [UIImage imageNamed:#"backBtnWhite"];
_backButton = [UIButton buttonWithType:UIButtonTypeCustom];
_backButton.exclusiveTouch = YES;
[_backButton setImage:backButtonImage forState:UIControlStateNormal];
[_backButton setImage:backButtonImage forState:UIControlStateHighlighted];
[_backButton addTarget:self action:#selector(back) forControlEvents:UIControlEventTouchUpInside];
_backButton.frame = CGRectMake(-4.0, 7.0, 40, 40);
UIView * view = [[UIView alloc] initWithFrame:CGRectMake(-4.0, 7.0, backButtonImage.size.width, backButtonImage.size.height)];
[view addSubview:_backButton];
UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
self.navigationItem.rightBarButtonItem = barButtonItem;
But after that I've added UIView it became difficult to click on it.
I've tried to change button area, but it didn't give results.
It seems like you specify incorrect frame for _button or/and view and this changes the tap region. Try to set different background color for _backButton and view. This will show you if frames are in positions that you expect.
If you try to adjust image position by specifying these frames - its more correct to use properties of UIButton like imageEdgeInsets and titleEdgeInsets.
In my RootViewController, I set an image as a rightBarButtonItem exactly like this, but in this second ViewController I created, this "back" button won't display. I run it on the simulator and on my iPhone and I can tap the area where the button should be showing up and the goBack{} method is being called. Just the image(s) aren't showing. Any thoughts?
Also, as far as the images go, I'm exporting them the exact same way I've been exporting all my other images. All the others have been working fine. It doesn't seem like a problem with the images, as I've substituted the back button images for several others and none of them are displaying. I'm pretty sure I'm creating these images in the right order. Anyway, any help would be much appreciated.
From my NewGameViewController.m file:
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat startingPoint = 0.0;
CGRect bounds = self.view.bounds;
bounds.origin.y = startingPoint;
bounds.size.height -= startingPoint;
self.ivarNewGameTableView = [[UITableView alloc] initWithFrame:bounds style:UITableViewStyleGrouped];
self.ivarNewGameTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.ivarNewGameTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.ivarNewGameTableView];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];
self.ivarNewGameTableView.backgroundView = backgroundImageView;
UIImage *mainNavBar = [UIImage imageNamed:#"startNewGameNavBar.png"];
UIImageView *navBarView = [[UIImageView alloc] initWithImage:mainNavBar];
[navBarView setFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
[self.navigationController.navigationBar addSubview:navBarView];
UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
// neither one of the following lines work. I know they're supposed to be identical, but
// seen Xcode be picky.
back.frame = CGRectMake(0.0, 0.0, 54.0, 36.0);
// [back setFrame:CGRectMake(0.0, 0.0, 54.0, 36.0)];
[back addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
[back setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[back setImage:[UIImage imageNamed:#"backTouched.png"] forState:UIControlStateHighlighted];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:back];
self.navigationItem.leftBarButtonItem = backButton;
}
-(void)goBack {
NSLog(#"goBack called.");
// [self.ivarNewGameTableView pushViewController:];
}
If you're in a navigation stack the back button item is set for you. Use backBarButtonItem correctly do get the behaviour you like.
On your main view controller that you want to come back to you need to overload the backBarButton item.
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: #"Back" style: UIBarButtonItemStyleBordered target: nil action: nil];
self.navigationItem.backBarButtonItem = newBackButton;
Some code samples on github
Ok. Finally figured this out. The problem was with this line in the creation of my New Game View Navigation Bar:
[self.navigationController.navigationBar addSubview:navBarView];
I did this in my Root View's Navigation Bar too. It now looks like this, in both files:
UIImage *startNewGameNavBar = [UIImage imageNamed:#"startNewGameNavBar.png"];
// with the key line being this one
[self.navigationController.navigationBar setBackgroundImage:startNewGameNavBar forBarMetrics:UIBarMetricsDefault];
In noob-speak, as I wish more programmers would use for noobs like me, if you're having problems with buttons disappearing behind the your custom navigation bar, and you're using code that looks like my example, it's because you're not supposed to be adding it as a subview. This puts it on top of everything else. Instead, change the navigation bar's background image and, of course, the image winds up in the...(drumroll, please)...background. (end drumroll)
As for the adding of the button images, I just created a UIButton with custom type, set the images for the normal and highlighted states, set the button to call my goBack method, set its frame size, and assigned it to the navigationItem's leftBarButtonItem property:
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:#"backTouched.png"] forState:UIControlStateHighlighted];
[backButton addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
[backButton setFrame:CGRectMake(0.0, 0.0, 54.0, 36.0)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];