ナビゲーションバーの戻るボタン長押しで一番上の階層に戻るようにしてみる

TweetBotだとつぶやきから他の人のプロフィール見てそのつぶやきみてっていうようにどんどん深い階層に降りていった時、左上の戻るボタンを長押しすると一番上の階層に戻ることができる。

普通のナビゲーションバーではそんなこと出来ないのでちょっとやってみた。

まずUINavigationControllerを継承してUILongTapGestureRecognizerをnavigationBarにセットする。

- (void)viewDidLoad
{
    [super viewDidLoad];
        UILongPressGestureRecognizer *longTap = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(backToRoot:)];
        longTap.delegate = self;
        [self.navigationBar addGestureRecognizer:longTap];
}

navigationBarに追加する理由は、中のビューに触れないので。
delegateを指定して戻るボタン以外のところではジェスチャーをキャンセルするようにする。

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    UIView *v = self.navigationBar.subviews.lastObject;
    if ([[[v class] description].lowercaseString rangeOfString:@"itembutton"].location != NSNotFound) {
        CGPoint p = [gestureRecognizer locationInView:v];
        if (CGRectContainsPoint(v.bounds, p)) {
            return YES;
        }
    }
    return NO;
}

GestureRecognizerのdelegateに指定しているのでdelegateメソッドを実装する。
今のところ戻るボタンはUINavigationBarの一番上に置かれるようなので
navigationBarの一番上のビューを取得する。
これもまた今のところバックボタンはUINavigationBarItemButtonViewというプライベートなクラスなので
クラス名からitembuttonがあるかをチェックし(そのままクラス名を使うとリジェクトされそうなので)、
タップ位置がそのボタンの中に存在すればYESを返しGestureRecognizerを開始する。

あとは長押しの通知を受けたら一番上の階層に戻すメソッドを実装するだけ

-(void) backToRoot:(UIGestureRecognizer *)recog
{
    if (recog.state == UIGestureRecognizerStateBegan) {
        [self popToRootViewControllerAnimated:YES];        
    }
}

まあ個人的には4階層以上深い階層になるんだったらアプリの設計を考え直したほうが
良いと思ってる口なんですが、やむを得ずそういう仕様になったときは
こういう小ネタを仕込んどくと良いかもしれません。