UINavigationBar UIBarButtonItems much larger click area than required - iphone

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)

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.

iPhone: How to make UIButton which you can easily tap?

I'm making customized navigation bar by using UIView and UIButtons instead of UINavigationBar.
But my UIButtons on the navigation bar doesn't response sensitively.
I have to tap almost center of the UIButton to tap.
It doesn't respond if I tap edge of the UIButton.
But buttons on normal UINavigationBar can be tapped by tapping edge of the button.
Even by tapping outside of the button, it can be tapped.
Shutter button or Option button on the camera app also can be tapped by tapping edge or outside of buttons.
How can I implement those easily tappable buttons to my app?
Use an image and create a custom button. Set the button so the image does not scale to the size of the button's view, but instead will just Center. Expand the button's size so it is larger than the image on each side. Apple does this as well with things like tab buttons.
UIButton has an imageEdgeInsets property specially for this purpose. Just make a UIButton frame as big as you need for touchable area and than scale image inside it appropriately, using imageEdgeInsets.
Disclaimer: This code has not been tested, but it gives you an idea of how it could be done.
You make a button (in this case 40px x 40px), and then add a background image to it which is smaller, hence gives the impression of that the image is very "clickable".
// This image is 20px x 20px (Just an example)
UIImage* backgroundImage = UIImage imageNamed:#"backgroundImage.png"]
// Custom button, remember to add a target method
UIButton* customButton = [UIButton buttonWithType:UIButtonTypeCustom];
customButton.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
customButton.contentMode = UIViewContentModeCenter;
[customButton setImage:backgroundImage forState:UIControlStateNormal];
UIBarButtonItem* customBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customButton];
self.navigationItem.rightBarButtonItem = customBarButtonItem;
[customBarButtonItem release];

how to add UINavigationController UIToolbar to the top of the view not the bottom

I am trying to add a the UINavigationController UIToolbar to the top of the view, (under the navigation controller.
My view is a UITableViewController... so there is that to deal with. Currently I am just positioning the UIToolbar that appears at the bottom of the view where I want it to display using
[self.navigationController.toolbar setFrame:CGRectMake(0, 60, 320, 30)];
this positions the toolbar in the correct place I would like it to appear, However there is a problem with where its positioned, which I will explain.
When you set a UINavigationController toolbar to be displayed it puts itself at the bottom of the view and pushes the UITableView up so the toolbar does not cover the tableview. However when I change the position of the toolbar the tableview still thinks the toolbar is at the bottom of the screen meaning the toolbar does not meet flush at the bottom of the screen how I would like it too.
So my question is how can I get the toolbar to display directly below the navigation controller bar and push the tableview down abit to accommodate for the toolbar in its new position.
I hope this all makes sense, Any help I would like to than in advance and below is the current code I am using (all be it basic I am still abit perplexed about whats going on behind the scenes for this to happen).
- (void) viewDidLoad
{
//..
[self.navigationController setToolbarHidden:NO animated:YES];
[self.navigationController.toolbar setFrame:CGRectMake(0, 60, 320, 30)];
self.navigationController.toolbar.tintColor = [UIColor lightGrayColor];
//..
}
update
this is currently what the toolbar is doing to my tableview
Call setFrame on your UITableView to move it into positon.
float y = self.navigationController.toolbar.frame.origin.y + self.navigationController.toolbar.frame.size.height;
[myTable setFrame:CGRectMake(0, y, self.view.frame.size.width, self.view.frame.size.height-y)];

uinavigationbar moves up when returning from inactive state

I have a custom image header ABOVE the uinavigationbar. I do this with this code:
self.navController.view.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.navController.navigationBar.frame = CGRectMake(0.0, 73.0, 320.0, 44.0);
UIView *checkNav = [self.navController.view viewWithTag:9999];
if (checkNav == nil) {
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"header-logo.jpg"]];
imgView.tag = 9999;
[imgView setFrame:CGRectMake(0.0, 23.0, 320.0, 50.0)];
[self.navController.view addSubview:imgView];
}
This works great. But, when the app goes inactive (hit the main iphone home button) and then you go back into the app, the uinavigationbar shifts up to the default place at the top of the screen and is hidden behind my custom header image.
I've tried throwing code into applicationDidBecomeActive, but it doesn't help. Any help would be greatly appreciated.
Thanks!
You do get warned about this in the docs:
With only a few exceptions, you should never modify the navigation bar object directly. It is permissible to modify the barStyle or translucent properties of the navigation bar but you must never change its frame, bounds, or alpha values directly
Having said that, you should be able to get the effect by repeating your above code in applicationDidBecomeActive or applicationWillEnterForeground, assuming you have a pointer to the navigation controller from there. You may find that hiding and re-showing the navigation bar is required (bit of a hack, but it can help - I had a similar problem with hiding / showing the status bar and the navigation bar not going to its correct place)
What about putting the code into viewWillAppear?

Why there is no separate line under Navigation bar?

I have a concern about the separate line under navigation bar. Please take a look at below screenshot
At the "Overview Settings" screen, I implement UITableViewController, and I see the separate line appear natively. But at "Overview" screen, I implement UIViewController and this line is not appear. How do I make it appear on every screen without add a customized view to fake this line?
Thanks so much!
Its because What I think is that the above one is navigation bar and below one is tabbar.
Kindly cross check it once again.
Or try changing the tint color of the navigation bar and check whehter its working or not.
hAPPY cODING...
After researching, I found a best way to do this is add an UIView as a subview of UINavigationBar as following code:
CGRect navFrame = [navBar bounds];
UIView *separateLine = [[UIView alloc] initWithFrame:CGRectMake(0, navFrame.size.height - 2, navFrame.size.width, 1)];
separateLine.backgroundColor = RGB(38,38,38);
separateLine.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
[navBar addSubview:separateLine];
[separateLine release];