9

I have an application with three buttons (actually UIView's) laid out horizontally underneath the navigation bar.

The three buttons are of substantial enough size (larger than the back button, for example), however they won't respond to touches when the hit is in the top third part, roughly.

I'm aware that this area directly underneath the nav bar is sort of 'reserved' for the back button and other UINavigation items having a touch area that expands beyond the navigation bar (by quite a significant margin), however in some instance there isn't even a navigation item nearby to steal the event, and my views still don't respond.

The weird thing is that I am getting a call to the hitTest method in my UIView, just never a touchesBegan/Ended/etc.

The result is that it is very difficult to press the buttons and if one is anywhere near a UINavigationItem, the item will steal the event, even though in hitTest I am returning the correct UIView to the system.

Unfortunately I am the implementer and not the designer so a design change is a last resort.

Any ideas? Thanks!

Sam
  • 3,659
  • 3
  • 36
  • 49
  • I didn't find the solution, but found the reasons why this happens. [Link here](http://stackoverflow.com/questions/9079907/why-uinavigatinbar-steals-touch-events) – Alexander Mar 19 '12 at 05:07

4 Answers4

9

You have to subclass your UINavigationBar and in your CustomNavigationBar do this:

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    if ([self pointInside:point withEvent:event]) {
        self.userInteractionEnabled = YES;
    } else {
        self.userInteractionEnabled = NO;
    }

    return [super hitTest:point withEvent:event];
}

Info about how to subclass UINavigationBar you can find here.

Community
  • 1
  • 1
Andrei Radulescu
  • 1,989
  • 1
  • 17
  • 29
  • 1
    Looks good, can anyone confirm this works? I'm no longer on the same project – Sam May 15 '12 at 10:54
  • As of iOS 6.1, this only works on the Simulator. My 5th-gen iPod Touch continues to "steal" touches near the navigation bar. – Jonas Alves Mar 24 '13 at 01:32
  • 1
    Unfortunately, in modern iOS variants, the hitTest is called and you can see the event is not inside the navbar, but the userInteractionEnabled = NO seems to have no bearing on the event passing through. – rcw3 Jun 26 '13 at 22:07
  • What ridiculous default behaviour. Thanks for nothing Apple, have spent half a day figuring this out. – Reuben Scratton Oct 03 '14 at 16:31
4

The code provided by @Andrei will cause controller stack inconsistency on iOS 7 when quickly push and pop controllers. The system itself will change the userInteractionEnabled property before and after push/pop animations. Here is how I fixed this issue.

@interface DMNavigationBar ()

@property (nonatomic, assign) BOOL changingUserInteraction;
@property (nonatomic, assign) BOOL userInteractionChangedBySystem;

@end

@implementation DMNavigationBar

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (self.userInteractionChangedBySystem && self.userInteractionEnabled == NO)
    {
        return [super hitTest:point withEvent:event];
    }

    if ([self pointInside:point withEvent:event])
    {
        self.changingUserInteraction = YES;
        self.userInteractionEnabled = YES;
        self.changingUserInteraction = NO;
    }
    else
    {
        self.changingUserInteraction = YES;
        self.userInteractionEnabled = NO;
        self.changingUserInteraction = NO;
    }

    return [super hitTest:point withEvent:event];
}

- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
{
    if (!self.changingUserInteraction)
    {
        self.userInteractionChangedBySystem = YES;
    }
    else
    {
        self.userInteractionChangedBySystem = NO;
    }

    [super setUserInteractionEnabled:userInteractionEnabled];
}

@end
nonamelive
  • 6,510
  • 8
  • 40
  • 47
  • 1
    This fails in iOS 8. If you have a UISearchField or something else that needs to be first responder in the UINavigationBar, then this will constantly lock up the UI and disable the keyboard. – strange Jun 23 '14 at 11:08
  • @strange I haven't tried this on iOS 8. Would you please file a bug report to Apple for this issue? Thank you! – nonamelive Jun 23 '14 at 18:35
  • @strange Are you still having this problem? I don't see any problems on iOS 8 GM. – nonamelive Sep 16 '14 at 00:35
  • @strange Try this to see if it works for you when you have a UISearchBar as a subview of your navigation bar. https://gist.github.com/nonamelive/8287674ea7cfc1a9d2ff – nonamelive Sep 21 '14 at 05:44
1

Had the same problem and the above answers are good and they point to a concise solution.

The same answer for the swift version:

  class APLVNavigationBar: UINavigationBar {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    if pointInside(point, withEvent: event){
        userInteractionEnabled = true
    }else{
        userInteractionEnabled = false
    }
    return super.hitTest(point, withEvent: event)
  }
}
kandelvijaya
  • 1,545
  • 12
  • 20
-2

I dont think that it is the problem, but did you bring the button subviews to the front?

[self.view bringSubviewToFront:buttonView]