Adjust vertical position of UIBarButtonItem title inside UIToolbar - swift

Is it possible to adjust the title of a UIBarButtonItem inside an UIToolbar?
I've tried the following lines of code without success:
UIBarButtonItem.appearance().setTitlePositionAdjustment(UIOffset(horizontal: 30, vertical: 30), forBarMetrics: UIBarMetrics.Default)
And this one:
self.setTitlePositionAdjustment(UIOffset(horizontal: 30, vertical: 30), forBarMetrics: UIBarMetrics.Compact)

Offset an UIBarButtonItem (Text!) inside a UIToolBar vertically seems not to work with Xcode 7 (Beta 6).
You are right, according to the docs, this should do it:
UIBarButtonItem.appearance().setTitlePositionAdjustment
either globally in AppDelegate:
UIBarButtonItem.appearance().setTitlePositionAdjustment(UIOffset(horizontal: 30, vertical: 30), forBarMetrics: UIBarMetrics.Default)
or locally with the item outlet itself:
(Swift)
self.myBarItem.setTitlePositionAdjustment(UIOffset(horizontal: 30, vertical: 30), forBarMetrics: UIBarMetrics.Default)
(Obj C)
[self.myBarItem setTitlePositionAdjustment:UIOffsetMake(30, 30) forBarMetrics: UIBarMetricsDefault];
Is this a bug with Appearance?

Change the baselineOffset of the text.
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(self.someFunction))
let attributes = [NSAttributedString.Key.baselineOffset: NSNumber(value: -3)]
doneButton.setTitleTextAttributes(attributes, for: .normal)
A negative baselineOffset will shift the title text down, a positive value will shift it up. 0 is the default value.
I tested this on iOS 11.

I was frustrated with this too so I subclassed UIBarButtonItem to work as expected. Taking from this answer I came up with this:
#interface TitleAdjustingBarButtonItem(){
UIView* _parentView;
UIButton* _button;
}
#end
#implementation TitleAdjustingBarButtonItem
-(id) initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action{
_button = [[UIButton alloc] init];
[_button setTitle:title forState:UIControlStateNormal];
[_button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[_button sizeToFit];
_parentView = [[UIView alloc] initWithFrame:_button.bounds];
[_parentView addSubview:_button];
return [super initWithCustomView:_parentView];
}
#pragma mark - property setters -
-(void) setTitlePositionAdjustment:(UIOffset)adjustment forBarMetrics:(UIBarMetrics)barMetrics{
[_button sizeToFit];
_parentView.bounds = CGRectMake(0.0, 0.0, _button.bounds.size.width - (adjustment.horizontal * 2.0), _button.bounds.size.height - (adjustment.vertical * 2.0));
}
Admittedly it does not account for the different bar metrics but for default metrics I've gotten this to work pretty well.

I found a workaround. I was trying to change the position of the back button title in the navigation bar. The title seems to be fixed in the lower left corner of it. So I changed the position of the whole navigation bar and adjusted its size accordingly so it would seem to be the same size in the view.
let xDisplacement: CGFloat = -25 // Adjust horizontal position
let yDisplacement: CGFloat = 9 // Adjust vertical position
let h = (self.viewIfLoaded?.frame.size.height)!/12 //Set your height in reference to the size of the view
let w = (self.viewIfLoaded?.frame.size.height)!//Set your width in reference to the size of the view
//Change placement adjusting size for change.
self.navigationController?.navigationBar.frame = CGRect(x: xDisplacement, y: yDisplacement, width: w-xDisplacement, height: h-yDisplacement)
In my case the background is transparent so the adjusting doesn't really make a difference. Also I was trying to move the title left. I think that is the only situation where de adjusting is useful.
Anyway, just a workaround, but I hope it helps.
Total noob here, feedback appreciated.

Suggestion for this: Use UIButton inside of UIBarButtonItem.
I have a toolbar that has two taller buttons in it on either side (Up Button and Down Button in the screenshot) and when the user initiates an action I show two buttons in the toolbar that only have text on them. When I originally put them on the toolbar as UIBarButtonItem objects, they sat very low on the toolbar and nothing I tried would get them to sit higher. Their position made it hard for the user to press them on iPadPro devices because the bar at the bottom of the device was kind of blocking them.
I found that if I dragged a UIButton to the toolbar, IB would automatically put those inside a UIBarButtonItem as the view, and they were centered vertically in the toolbar. I was then able to adjust the content Insets for the UIButton like I normally would and the change was reflected properly. (Since I was only looking to get them centered vertically, merely using UIButtons worked for me). Remember that any IBActions you hook up should be hooked up to the UIButtons.
I did this with XCode 10.1 and swift 4.

Related

Displaying both a right view and a clear button in UITextField

After adding a right view to a UITextField, I am finding that it refuses to display both the right view and the clear button (having both rightViewMode and clearButtonMode set to UITextFieldViewModeAlways). I see the right view but the clear button is no longer displayed. I made sure that they don't overlap by having overriden clearButtonRectForBounds and clearButtonRectForBounds, to no avail. And if I use the leftView instead of rightView, then no such issue occurs and both the left view and the clear button are displayed.
So although it does not appear to be stated in the documentation, it looks to me like the clear button is only displayed when the right view isn't displayed (and when the text property isn't a blank string). Is this correct and does anyone have a reliable workaround? In the meantime I believe I am stuck with having to create a UIView that overlays my right view on top of a UITextField in order to get what I though I'd be getting from UITextField alone.
you can't display both at the same time , but you can do it like this
UITextField * textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 100, 300, 40)];
[textfield setBorderStyle:UITextBorderStyleRoundedRect];
UIImageView * imgvw = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"search.jpeg"]];
[imgvw setFrame:CGRectMake(0, 0, 30, 30)];
[textfield setRightView:imgvw];
[textfield setRightViewMode:UITextFieldViewModeUnlessEditing];
[textfield setClearButtonMode:UITextFieldViewModeWhileEditing];
[self.view addSubview:textfield];
Yes you are right. UITextfield has properties like left and right view. If you use clear button it overlaps rightview.
Form Apple doc http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITextField_Class/Reference/UITextField.html
If your overlay view does not overlap any other sibling views, it receives touch events like any other view. If you specify a control for your view, that control tracks and sends actions as usual. If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
But like the name says it is a view. So you can create your own view which has 2 buttons on it and set rightview of the textfield. If you wish, you can use delegate methods of the textfield to make your buttons appear and dissapear from the view.
I had the same issue but was easily solved by 'fooling' the UITextField into using the LeftView instead of RightView. This way you can use both clear button and your supposed rightView. All you need to do is subclass UITextField and return the 'right corner' in leftViewRectForBounds and similarly update editingRectForBounds and textRectForBounds. Works perfectly.
I'll back up #strange 's answer (using left view instead of right) with some code:
-(CGRect)leftViewRectForBounds:(CGRect)bounds
{
return CGRectOffset([super leftViewRectForBounds:bounds], bounds.size.width - 30, 0);
}
-(CGRect)clearButtonRectForBounds:(CGRect)bounds
{
return CGRectOffset([super clearButtonRectForBounds:bounds], -30, 0);
}
-(CGRect)editingRectForBounds:(CGRect)bounds
{
CGRect rect = bounds;
rect.origin.x = 10;
rect.size.width -= 60;
return rect;
}
-(CGRect)textRectForBounds:(CGRect)bounds
{
CGRect rect = bounds;
rect.origin.x = 10;
rect.size.width -= 60;
return rect;
}
Note that my right (left) view width is 30.
If you happen to use both left and right view, and clear button, then this solution obviously won't work. In that case you will have to give up using one of those, and use a separate view put next to your UITextField.

UINavigationItem centering the title

I have a navigationBar with both Left and Right bar buttons on each side. I have a customTitlelabel which I set as the titleView of the UINavigationItem.
[self.navigationItem setTitleView:customTitleLabel];
All is fine now. The problem, the size of the rightbarButton is dynamic based on the input I get in one of the text fields.
Therefore the title is automatically centered based on the available space between the buttons.
how can i set the title to a fixed position?
Setting the titleView property of the nav bar works just fine - no need to subclass or alter any frames other than those of your custom view.
The trick to getting it centered relative to the overall width of UINavigationBar is to:
set the width of your view according to the size of the text
set the alignment to centered and
set the autoresizingmask so it gets resized to the available space
Here's some example code that creates a custom titleView with a label which remains centred in UINavigationBar irrespective of orientation, left or right barbutton width:
self.title = #"My Centered Nav Title";
// Init views with rects with height and y pos
CGFloat titleHeight = self.navigationController.navigationBar.frame.size.height;
UIView *titleView = [[UIView alloc] initWithFrame:CGRectZero];
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
// Set font for sizing width
titleLabel.font = [UIFont boldSystemFontOfSize:20.f];
// Set the width of the views according to the text size
CGFloat desiredWidth = [self.title sizeWithFont:titleLabel.font
constrainedToSize:CGSizeMake([[UIScreen mainScreen] applicationFrame].size.width, titleLabel.frame.size.height)
lineBreakMode:UILineBreakModeCharacterWrap].width;
CGRect frame;
frame = titleLabel.frame;
frame.size.height = titleHeight;
frame.size.width = desiredWidth;
titleLabel.frame = frame;
frame = titleView.frame;
frame.size.height = titleHeight;
frame.size.width = desiredWidth;
titleView.frame = frame;
// Ensure text is on one line, centered and truncates if the bounds are restricted
titleLabel.numberOfLines = 1;
titleLabel.lineBreakMode = UILineBreakModeTailTruncation;
titleLabel.textAlignment = NSTextAlignmentCenter;
// Use autoresizing to restrict the bounds to the area that the titleview allows
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
titleView.autoresizesSubviews = YES;
titleLabel.autoresizingMask = titleView.autoresizingMask;
// Set the text
titleLabel.text = self.title;
// Add as the nav bar's titleview
[titleView addSubview:titleLabel];
self.navigationItem.titleView = titleView;
You can't do what you want directly -- the position of your title view is out of your control (when managed by UINavigationBar).
However, there are at least two strategies to get the effect you want:
1) Add the title view not as the 'proper' title view of the nav bar, but as a subview of the UINavigationBar. (Note: this is not 'officially' sanctioned, but I've seen it done, and work. Obviously you have to watch out for your title label overwriting bits of the buttons, and handle different size nav bars for different orientations, etc. -- a bit fiddly.)
2) Make an intelligent UIView subclass that displays a given subview (which would be your UILabel) at a position calculated to effectively show the subview perfectly centered on the screen. In order to do this, your intelligent UIView subclass would respond to layout events (or frame property changes etc.) by changing the position (frame) of the label subview.
Personally, I like the idea of approach 2) the best.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.topItem?.title = ""
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationItem.title = "Make peace soon"
}
The right answer is to override sizeThatFits: of your custom titleView and return its content size. Navigation bar centers custom title view until it has no space left to do that.
For example if you have UIView container with UILabel inside:
#interface CustomTitleView : UIView
#property UILabel* textLabel;
#end
#implementation CustomTitleView
- (CGSize)sizeThatFits:(CGSize)size {
CGSize textSize = [self.textLabel sizeThatFits:size];
CGSize contentSize = size;
contentSize.width = MIN( size.width, textSize.width );
return contentSize;
}
#end
I tried aopsfan's answer but it didn't work. A breakpoint revealed that the bar's center was "(480.0, 22.0)" (The X coordinate way off) .
So I changed it into this:
- (void)layoutSubviews
{
[super layoutSubviews];
// Center Title View
UINavigationItem* item = [self topItem]; // (Current navigation item)
[item.titleView setCenter:CGPointMake(160.0, 22.0)];
// (...Hard-coded; Assuming portrait iPhone/iPod touch)
}
...and it works like a charm. The slide/fade effect when pushing view controllers is intact. (iOS 5.0)
I had similar problem.
My solution is do hide the original back button, add add your own implementation. Since the system will reserve space for the left items.
UIImage* cancelIcon = [UIImage imageNamed:#"ic_clear"];
UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc] initWithImage:cancelIcon style:UIBarButtonItemStylePlain target:self action:#selector(back:)];
and the selector is simple
- (void)back:(UIButton *) sender
{
[self.navigationController popViewControllerAnimated:YES];
}
now it looks like this:
oh...and don't forget to use autolayout in your custom title view if you have dynamic length content like label in it. I add an additional layout in the customview to give it like "wrap_content" in Android by setting it centered to parent , and leading and trailing space ">=" 0
I had a similar situation where a titleView should be centered in UINavigationBar. I like occulus's approach of subclassing a UIView and overriding setFrame:. Then, I can center the frame inside the dimensions of UINavigationBar.
In the UIView subclass:
-(void)setFrame:(CGRect)frame{
super.frame = CGRectMake(320 / 2 - 50, 44 / 2 - 15, 100, 30);
}
The UIView subclass can then be assigned normally to titleView for each navigationItem. The developer does not have to programmatically add and remove special subviews from UINavigationBar.

Placing a custom view based UIBarButtonItem in the navigation bar without default horizontal padding

How do I remove the horizontal padding to the left and right of custom left and right UINavigationBar items? There seems to be ~ 10 points of padding that iOS sets by default.
I'm customizing left and right navigation bar buttons (I have given up on trying to set my own backButtonItem, so I'm just using the leftBarButtonItem).
In either case (left or right), pressing these custom buttons indicates that Apple seems to preserve some padding to the left of the leftBarButtonItem, and to the right of the rightBarButtonItem; regardless of how wide I make the custom background and image properties of the UIButton I place inside the left/right bar button item as its custom view.
Since UIBarButtonItems have no "frame" I can access, I can't position them within their superview like I can normal UIViews.
Any suggestions on how to remove this default padding? See screen shot attached to see the bit I'm trying to reduce to a zero width. In the screen shot, the plus icon appears shifted to the right because I gave it an inset; but the highlighted background image, also presumably using that inset, is getting clipped on its right side).
See image at: https://skitch.com/starbaseweb/rj2e5/ios-simulator
For reference, here's how I'm creating my custom UIBarButtonItem (in this case, it's the right button):
- (UIBarButtonItem *)customAddButtonItemWithTarget:(id)target action:(SEL)action {
UIButton *customButtonView = [UIButton buttonWithType:UIButtonTypeCustom];
customButtonView.frame = CGRectMake(0.0f, 0.0f, 45.0f, 44.0f);
[customButtonView setBackgroundImage:
[UIImage imageNamed:#"bgNavBarButton-OutsideRight-Normal.png"]
forState:UIControlStateNormal];
[customButtonView setBackgroundImage:
[UIImage imageNamed:#"bgNavBarButton-OutsideRight-Highlighted.png"]
forState:UIControlStateHighlighted];
[customButtonView setImage:
[UIImage imageNamed:#"bgNavBarButton-Add-Normal.png"]
forState:UIControlStateNormal];
[customButtonView setImage:
[UIImage imageNamed:#"bgNavBarButton-Add-Highlighted.png"]
forState:UIControlStateHighlighted];
[customButtonView addTarget:target action:action
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customButtonItem = [[[UIBarButtonItem alloc]
initWithCustomView:customButtonView] autorelease];
[customButtonView setImageEdgeInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 0.0f)];
//customButtonItem.imageInsets = UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 0.0f);
return customButtonItem;
}
55As commented above, the solution I went with is based on this answer to a different, but very much related question: How to adjust UIToolBar left and right padding. It is also facilitated by (and depends on) iOS5, which allows you to set multiple buttons on the left or right side, instead of just one.
Here's an example of removing the padding to the left of a custom left button item:
UIBarButtonItem *backButtonItem // Assume this exists, filled with our custom view
// Create a negative spacer to go to the left of our custom back button,
// and pull it right to the edge:
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
negativeSpacer.width = -5;
// Note: We use 5 above b/c that's how many pixels of padding iOS seems to add
// Add the two buttons together on the left:
self.navigationItem.leftBarButtonItems = [NSArray
arrayWithObjects:negativeSpacer, backButtonItem, nil];
And with this, the left padding for the left bar button item in a navigation bar, is gone!
NOTE: This has worked for me in iOS5 and iOS6. Given that iOS7 is considerably different (from the public demos), those of you with the early seeds of iOS7 should test if something so unintentional, like this hack, will actually continue to work for you beyond iOS6.
I have tried this and it works:
1) Create a custom UIToolbar subclass, which does nothing in -drawRect:, and is not opaque, and has backgroundColor = [UIColor clearColor].
2) Create a custom UIBarButtonItem with the toolbar as the custom view.
3) Add your buttons to the custom toolbar.
4) In your custom toolbar override -layoutSubviews and do your own spacing.

UIBarButtonItem has a too large click area

I know some have already asked the question but so far, the answers are not really specific.
Apparently apple have made the click area of the NavigationBar larger than it really is, but I don't think it's supposed to be that large.
In my app, there is a TableView right underneath the NavBar and you can click all the way down to half of the first cell to trigger the event of the rightBarButtonItem.
the button is instanced like this:
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
[[self navigationItem] setRightBarButtonItem:editButton];
"self" is the root ViewController of a NavigationController.
As you could imagine, it's a problem since the cells are selectable to push another ViewController.
I managed to go around the problem by making the cells' height bigger but I'd rather have them at the regular size.
I'm sure I'm not the only one with this case of scenario.
Thanks in advance.
UIButton *menuButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[menuButton addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[menuButton setImage:[UIImage imageNamed:#"menuIcon"] forState:UIControlStateNormal];
UIView *menuButtonContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[menuButtonContainer addSubview:menuButton];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:menuButtonContainer];
This perfectly worked for me....
You can't do much about the touch area. It's all based on how much finger area is picked up by the display. You can calculate size of touch area (warning: undocumented api) but intercepting touch events and calling a method to correct for fat finger syndrome seems a bit over-the-top.
Basic process is:
You touch screen
Change of capacitance is measured in a unique, individualized portion of the screen and sent to hardware/software to interpret (just as an aside, Apple's patent states as many as 15 touches can be tracked at once.. 10 fingers, two palms, and 3 others in case you ever make an iPhone "Twister" game)
Software interprets gradients in capacitance on screen and identifies touch boundaries
Software calculates centroid of touch based on the average capacitance magnitude for each pixel or point in the region
Software sends coordinates of touch boundary locations into application
Your app interprets as a touch event, it gets sent along the responder chain, and (hopefully) the user see's the touch on the screen
The other answer here from iosDeveloper about putting a container view around the button can help reduce the size of the clickable area on the left and right of the button, but doesn't help with the issue I had where you would activate the button clicking below the navigation bar.
After hours of searching several different SO threads and trying various things, I finally found my answer to this issue here: https://stackoverflow.com/a/10597699/1289240
Since i use a transparent toolbar and tint the UIBarButtonItem, I couldn't use a custom view as I would need to set tint, width/height, rounded corners to make it similar to UIBarButtonItem look
I therefore use a invisible button which I add to my items to resolve this issue. and events are trappped there
in #interface UIBarButtonItem (ImageButton)
+ (UIBarButtonItem*) invisibleButtonForTransparentBar {
static UIBarButtonItem* fakeBtn = nil;
if (!fakeBtn) {
fakeBtn = [[UIBarButtonItem alloc] initWithCustomView:[[UIView alloc] init]];
}
return fakeBtn;
}
in View Controller:
[self.viewToolbar setItems: #[flexSpace,
[UIBarButtonItem fakeLastButtonForTransparentBar],
self.btnTrackUser,
self.btnMapConfig,
[UIBarButtonItem fakeLastButtonForTransparentBar],
flexSpace] ];
We are unable to reduce the default uibarbuttonitem size. The idea is to make a uibutton, add to a uiview , then to uibarbuttonitem's custom view.
The size of the uiview containing uibutton will be the click area of the uibarbutton.
Here is my code:
self.navigationController?.navigationBarHidden = false
let navigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 64)) // Offset by 20 pixels vertically to take the status bar into account
navigationBar.backgroundColor = UIColor.blueColor()
navigationBar.delegate = self;
// Create a navigation item with a title
let navigationItem = UINavigationItem()
//menu button
let menubutton: UIButton = UIButton(frame: CGRectMake(0, 0, 30, 30))
menubutton.setImage(UIImage(named: "menu"), forState: UIControlState.Normal)
menubutton.addTarget(self, action: "btn_clicked", forControlEvents: UIControlEvents.TouchUpInside)
//menu button custom view
let leftView = UIView(frame: CGRectMake(0,0,30,30))
leftView.addSubview(menubutton)
//left uibarbutton
let leftItem:UIBarButtonItem = UIBarButtonItem(customView: leftView)
navigationItem.leftBarButtonItem = leftItem
//searchButton
let searchbutton: UIButton = UIButton()
searchbutton.setImage(UIImage(named: "search1x"), forState: UIControlState.Normal)
searchbutton.frame = CGRectMake(0, 0, 30, 30)
searchbutton.addTarget(self, action: "btn_clicked", forControlEvents: UIControlEvents.TouchUpInside)
//menu button custom view
let rightView = UIView(frame: CGRectMake(0,0,30,30))
rightView.addSubview(searchbutton)
//right uibarbutton
let rightItem:UIBarButtonItem = UIBarButtonItem(customView: rightView)
navigationItem.rightBarButtonItem = rightItem
// Assign the navigation item to the navigation bar
navigationBar.items = [navigationItem]
// Make the navigation bar a subview of the current view controller
self.view.addSubview(navigationBar)
Note:
Ex:- menu button is a UIButton and added to leftView. The leftItem is a UIBarButtonItem and is added to leftBarButtonItem of the navigationBar. The leftView is added as the custom view of leftItem. So, the click area of leftBarButtonItem of the navigationBar is the frame of leftView. i.e., frame: CGRectMake(0,0,30,30).
All I can offer is to create UIBarButtonItem with custom view
let view = CustomView()
let itemButton = UIBarButtonItem(customView: view)
Then override this view's touchesEnded
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if touches.count == 1,
let touch = touches.first {
let touchPoint = touch.location(in: self)
if bounds.contains(touchPoint) {
someAction()
}
}
}
Don't forget to make it user interaction enabled somewhere in the init isUserInteractionEnabled = true.

UINavigationBar UIBarButtonItems much larger click area than required

hopefully someone can help me out- iv'e scoured the net for the answer and cannot find one-
the UIBarButtonItems added to UINavigationBar have a much larger click area than required-
for example, open up any project you have a nav bar with buttons on- click anywhere between the end of the button and the title of the nav bar- the button clicks, when you clearly did not click on the button-
also try this- click underneath the nav bar, below the button, the button clicks for about 5+ pixels below the nav bar-
my problem is this-
i have added a custom header with buttons to a tableview- but when i click the buttons in the header, the UINavigationBar buttons trigger for those 5+ pixels instead of the buttons in the tableview header-
i did a test, and removed the buttons from UINavigationBar and what is interesting is that for the 5 pixels below the nav bar, the buttons in the header will not trigger even though there are no buttons in the nav bar-
its almost like the nav bar has reserved some 5+ pixels below itself as click space-
my question is this-
can someone tell me how to make the nav bar not grab those extra 5+ pixels for its buttons?
thanks very much ;)
This is the only solution I found. Create a container of the custom button:
//Create a container for the button
UIView *buttonContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 55, 44)];
//Create a smaller button
UIButton *closeButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 55, 25)];
[closeButton setTitle:#"Cancel" forState:UIControlStateNormal];
//center the title
closeButton.titleEdgeInsets = UIEdgeInsetsMake(23, 0, 0, 0);
[buttonContainer addSubview:closeButton];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:buttonContainer];
I'm not 100% sure but maybe you can get the location of the touch via the UITouch class?
UIBarButtonItem doesn't extend UIResponder but UINavigationBar does!
So if you subclass UINavigationBar and use this subclass in your app, maybe you can catch the coordinates of the touch and check if they are ok to you and then decide to apply either the navigation bar action or your button action (by some custom redirection).
Short answer is... should shouldn't try and get rid of them. It's about ease of use. The navigation bar at the top tends to mean people tap lower than you may expect. Always leave that gap there, or have a sufficiently large hit area that the user stabbing their finger towards the middle of your "below the nav bar" item will avoid the dead area.
As far as I know, it is impossible to turn off. If you have other buttons on the navigation bar, those click-spaces will not collide, but if you have button directly beneath the nav bar with no space at all in between, you're out of luck. Consider a small padding in the header and its buttons as a solution.
Trying to work around the UINavigation Bar padding may run you into trouble when you submit to the app store. It would be easier to add the padding to your custom heading. As a "fat thumber" I have learned to appreciate the HIG.
Quite old question, but maybe a solution is helpful to others too...
I've created a UINavigationBar subclass, that overrides just one method: 'hitTest:withEvent:'. When hitTest:withEvent is called, it checks wether the event has happened inside the frame of the navigation bar (pointInside:withEvent:) or not. In case, the event has happened outside, the userInteractionEnabled flag is set to NO so the event will be ignored by the navigation bar and its subviews.
In my case, the navigation bar subclass is inserted via IB, but of course is is also possible to insert it via 'UINavigationController initWithNavigationBarClass:toolbarClass:'
Header:
#interface MMMasterNavigationBar : UINavigationBar
#end
Implementation:
#implementation MMMasterNavigationBar
/*
hitTest:withEvent:
The hit area in for navigation bar button items is enlarged by default.
Other objects directly below the navigation bar doesn't receive tap events.
We avoid the default enlarging of the tappable area by disabling userInteraction
when the real tap is outside the navigation bar.
*/
-(UIView *)hitTest:(CGPoint)pPoint
withEvent:(UIEvent *)pEvent {
//FLog;
if ([self pointInside:pPoint
withEvent:pEvent]) {
//NSLog(#"User interaction enabled");
self.userInteractionEnabled = YES;
} else {
//NSLog(#"User interaction disabled");
self.userInteractionEnabled = NO;
}
return [super hitTest:pPoint
withEvent:pEvent];
}
#end
This can be done directly from a storyboard. Drag a UIView into each navigation item, set its background to clearColor, and size it. Drag a button into each UIView and size them to match.
var buttonContainer:UIView = UIView()
buttonContainer.frame = CGRectMake(0, 0, 32, 32)
var imagess:UIImage = UIImage(named: "noti#2x.png")!
var closeButton:UIButton = UIButton()
closeButton.setImage(imagess, forState: UIControlState.Normal)
closeButton.frame = CGRectMake(10, 5, 20, 20)
closeButton.contentMode = UIViewContentMode.Center
closeButton.titleEdgeInsets = UIEdgeInsetsMake(20, 0, 0, 0)
buttonContainer.addSubview(closeButton)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: buttonContainer)