Why does UINavigationBar steal touch events?


//  MMBNavigationBar.m
//  mmbang
//  Created by CuiPanJun on 14-11-25.
//  Copyright (c) 2014年 iyaya. All rights reserved.

#import "MMBNavigationBar.h"

#define kMaxCenterOffset 20

@implementation MMBNavigationBar

- (void)layoutSubviews{
    [super layoutSubviews];
    UINavigationItem *topItem = self.topItem;
    NSArray *leftItems = topItem.leftBarButtonItems;
    NSArray *rightItems = topItem.rightBarButtonItems;
    UIView *leftLastView = [leftItems.lastObject customView];
    UIView *rightFirstView = [rightItems.firstObject customView];
    UIView *titleView = nil;
    for (UIView *view in self.subviews) {
        if(topItem.titleView == view){
            titleView = view;
    if (titleView && leftLastView && rightFirstView) {
        titleView.layer.borderWidth = 1.0f;
        titleView.layer.borderColor = [UIColor redColor].CGColor;
        leftLastView.layer.borderWidth = 1.0f;
        leftLastView.layer.borderColor = [UIColor redColor].CGColor;
        rightFirstView.layer.borderWidth = 1.0f;
        rightFirstView.layer.borderColor = [UIColor redColor].CGColor;
        CGFloat emptyStartX = CGRectGetMaxX(leftLastView.frame);
        CGFloat emptyEndX = CGRectGetMinX(rightFirstView.frame);
        CGFloat leftMargin = CGRectGetMinX(titleView.frame) - emptyStartX;
        CGFloat rightMargin = emptyEndX - CGRectGetMaxX(titleView.frame);
        CGFloat centerOffset = CGRectGetMidX(self.bounds) - CGRectGetMidX(titleView.frame);
        CGFloat emptySpace = emptyEndX - emptyStartX;
        if (emptySpace > CGRectGetWidth(titleView.frame) && fabsf(centerOffset) >= kMaxCenterOffset) {
            CGFloat offsetAbs = fabsf(centerOffset);
            if (centerOffset > 0) {
                // 右边有空余空间
                if (rightMargin > 0) {
                    CGRect titleFrame = titleView.frame;
                    titleFrame.origin.x +=  MIN(offsetAbs, rightMargin) ;
                    titleView.frame = titleFrame;
            }else if(centerOffset < 0){
                // 左边有空余空间
                if (leftMargin > 0) {
                    CGRect titleFrame = titleView.frame;
                    titleFrame.origin.x -= MIN(offsetAbs ,leftMargin);
                    titleView.frame = titleFrame;

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView *view = [super hitTest:point withEvent:event];
    NSLog(@"point:%@ \nview:%@",NSStringFromCGPoint(point),view);
    return view;




It’s important to understand that these are compile-time constants, they are therefore not useful for detecting at runtime what platform or OS version you are running on (e.g. detecting if you are running on iPad vs iPhone).

What these constants do is allow you to detect at compile time whether the code is being built for a given SDK or deployment target. For example, if you wrote an open source library that contains code that only works when compiled against the iOS 5 SDK, you might include this check to detect which SDK the code is being compiled for:

//you can use iOS 5 APIs here because the SDK supports them
//but the code may still crash if run on an iOS 4 device
//this code can’t use iOS 5 APIs as the SDK version doesn’t support them
Or alternatively, if you want to see what the minimum OS version being targeted is…

//minimum deployment target is 5.0, so it’s safe to use iOS 5-only code
//you can use iOS5 APIs, but the code will need to be backwards
//compatible or it will crash when run on an iOS 4 device
This is different from detecting at runtime what OS you are running on. If you compile the code in the first example above using the iOS 4 SDK it will use your iOS 4-safe code, but won’t take advantage of any iOS 5 features when run on an iOS 5 device. If you build it using the iOS 5 SDK then set the deployment target to iOS 4 and try to run it on an iOS 4 device, it will compile and install fine but may still crash at runtime because the iOS 5 APIs aren’t there.

In the second example above, if you set your deployment target to iOS 4 or below then it will use the iOS 4-safe code path, but if you set the deployment target to iOS 5, it won’t run at all on an iOS 4 device (it will refuse to install).

To build an app that runs on iOS 4 and 5 and is still able to take advantage of iOS 5 features if they are available, you need to do run-time detection. To detect the iOS version at runtime you can do this:

if ([[[UIDevice currentDevice] systemVersion] compare:@”5.0.1″ options:NSNumericSearch] != NSOrderedAscending) {
//running on iOS 5.0.1 or higher
But that means keeping track of exactly which API features were added in which OS version, which is clunky and should only be done as a last resort. Usually, a better approach is to use feature detection, like this:

if ([SomeClass class]) {
//this class exists

if ([SomeClass instancesRespondToSelector:@selector(someMethod:)]) {
//this method exists
Also, to detect at runtime if you are on an iPad or iPhone, you can do this:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//on an ipad
Performing these checks at runtime allows you to create a single app that runs on multiple devices and iOS versions and is able to take advantage of the features of each platform.