I have an iOS app in which I add a UINavigationController
as a child view controller to the main screen. It's arranged vertically, so that the nav controller takes up a bottom part of the main view's height and is full-width. There is a button (actually a custom view with a tap recognizer) that sits right above the nav that is used to hide and show it; when the nav is hidden, the button is at the bottom of the main screen, and when the nav is shown the button is right above the top of the nav bar.
The issue is that the navigation bar appears to intercept touches on the bottom 10 points or so of the button. Why is this?
The code to add the child looks like this:
UIViewController *root = [[[UIViewController alloc]init]autorelease]; //some VC
UINavigationController *nav = [[[UINavigationController alloc]initWithRootViewController:root]autorelease];
[self addChildViewController:nav];
CGRect rect = self.view.frame;
CGFloat height = 500;
nav.view.frame = CGRectMake(0, rect.size.height - height, rect.size.width, height);
[self.view addSubview:nav.view];
[nav didMoveToParentViewController:self];
//Now the button
UIView *view = [[[UIView alloc]initWithFrame:CGRectZero]autorelease];
view.backgroundColor = [UIColor greenColor];
[self.view addSubview:view];
view.frame = CGRectMake(0, rect.size.height - 500 - 40, rect.size.width, 40);
UITapGestureRecognizer *recog = [[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(onToggleSplit)]autorelease];
[view addGestureRecognizer:recog];
onToggleSplit
animates the nav up and down. When it's up, taps on the bottom 10 points or so of the "button" do nothing. I tried overriding touchesBegan
on my custom "button" view, and that doesn't even get called.
If I use a regular UIViewController
(or a subclass thereof) for the child VC, the button responds as expected.
This has been asked and answered here. Basically, controls on iOS have "slop zones" designed to allow sloppy touch accuracy on the part of the user. I'm going to file a bug about how this shouldn't reach outside the control's view controller's bounds.
Update: filed as radar 19504573.
Update 2: my radar has been marked as a duplicate of 8088542, which is open. Also, my case is somewhat simple (one view right on top of a navigation bar), so I was able to overcome the issue. I overrode
hitTest:withEvent:
and noticed that when a tap happens, it gets called twice. On the first, the touch coordinates are what you expect, and callingsuper
returns the view you expect. The second has the sametimestamp
value on the event, but the y coordinate is higher (lower on the screen) andsuper
returns the nav bar. So:UIView
and add a propertyviewToWatch
.viewDidLoad
, grab the view, cast it, and set itsviewToWatch
to the view whose touch events are being filched.NSTimeInterval lastTimestamp
andBOOL isInView
.hitTest:withEvent:
. If calling super yieldsviewToWatch
, set thelastTimestamp
to the event's stamp andisInView
toYES
. Otherwise, ifsuper
returns a nav bar, if the event's stamp matches andisInView
is stillYES
, reset both properties and returnviewToWatch
.It's working so far.