单击手势识别器覆盖的UINavigationBar中的事件

时间:2011-10-14 20:24:56

标签: ios uitableview uinavigationbar uigesturerecognizer

首先,问题是:

当你有一个tableView如何实现时,用户可以点击NavigationBar一直滚动到顶部。

解决方案:

- (void)viewDidLoad {
    UITapGestureRecognizer* tapRecon = [[UITapGestureRecognizer alloc]
              initWithTarget:self action:@selector(navigationBarDoubleTap:)];
    tapRecon.numberOfTapsRequired = 2;
    [navController.navigationBar addGestureRecognizer:tapRecon];
    [tapRecon release];
}

- (void)navigationBarDoubleTap:(UIGestureRecognizer*)recognizer {
    [tableView setContentOffset:CGPointMake(0,0) animated:YES];
}

这就像魅力一样!

但是德拉罗克指出了一个问题:

只有在没有后退按钮或rightBarButtonItem时,此方法才可行。他们的点击事件被手势识别器

覆盖

我的问题:

我怎样才能拥有可导航我的NavigationBar可以点击但仍能使用我的应用程序中的后退按钮的好功能?

因此要么找到一个不会覆盖后退按钮的其他解决方案,要么找到一个解决方案让后退按钮恢复工作:)

6 个答案:

答案 0 :(得分:15)

我没有使用位置视图,而是通过检查UITouch的类来解决这个问题。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return (![[[touch view] class] isSubclassOfClass:[UIControl class]]);
}

请注意,导航按钮的类型为UINavigationButton,未公开,因此子类检查。

此方法位于您指定为手势识别器的委托的类中。如果您刚刚开始使用手势识别器,请注意委托是与目标分开设置的。

答案 1 :(得分:7)

UIGestureRecognizerDelegate有一个名为“gestureRecognizer:shouldReceiveTouch”的方法。如果你能够指出触摸的视图是否是按钮,只需让它返回“否”,否则返回“是”,你就可以了。

答案 2 :(得分:5)

UIGestureRecognizer也有一个属性@property(nonatomic) BOOL cancelsTouchesInView。来自文档:A Boolean value affecting whether touches are delivered to a view when a gesture is recognized.

所以,如果你只是做

tapRecon.cancelsTouchesInView = NO;

这可能是一个更简单的解决方案,具体取决于您的使用案例。这是我在我的应用程序中执行此操作的方式。

当按下导航栏中的按钮时,会执行其操作(根据需要),但也会执行UIGestureRecognizer的操作。如果这不打扰你,那么这将是我能想到的最简单的解决方案。

答案 3 :(得分:2)

iOS7版本:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    CGPoint point = [touch locationInView:touch.view];
    UINavigationBar *naviagationBar = (UINavigationBar *)touch.view;
    NSString *navigationItemViewClass = [NSString stringWithFormat:@"UINavigationItem%@%@",@"Button",@"View"];
    for (id subview in naviagationBar.subviews) {

        if (([subview isKindOfClass:[UIControl class]] ||
             [subview isKindOfClass:NSClassFromString(navigationItemViewClass)]) &&
             [subview pointInside:point withEvent:nil]) {

            return NO;
        }
    }
    return YES;
}

编辑:后退按钮手势一角的东西仍然被覆盖,所以你这个代码而不是pointInside:withEvent

CGRectContainsPoint((CGRect){ .origin = subview.frame.origin, .size = CGSizeMake(subview.frame.size.width + 16, subview.frame.size.height)}, point)

答案 4 :(得分:0)

Xamarin.iOS不会在私有API中公开Objective-C类的C#包装器,因此上面@ ben-flynn建议的整齐的子类检查在这里不起作用。

一种有点狡猾的解决方法是检查视图的Description字段:

navigationTitleTap = new UITapGestureRecognizer (tap => DidTapNavigationTitle());

navigationTitleTap.ShouldReceiveTouch = (recognizer, touch) => 
    touch.View.Subviews.Any(sv => 
        // Is this the NavigationBar's title or prompt?
        (sv.Description.StartsWith("<UINavigationItemView") || sv.Description.StartsWith("<UINavBarPrompt")) &&
        // Was the nested label actually tapped?
        sv.Subviews.OfType<UILabel>().Any(label =>
            label.Frame.Contains(touch.LocationInView(sv))));

NavigationController.NavigationBar.AddGestureRecognizer (navigationTitleTap);

Linq集合过滤器.OfType<T>在视图层次结构中捕获某些类型时非常方便。

答案 5 :(得分:0)

这对我有用,它基于斯塔瓦什的回答。我使用手势识别器的view属性在委托方法中返回YES / NO。

这是一个旧的应用程序,所以显然这不是ARC,不使用新的布局内容也不使用NSAttributed字符串。我把它留给你:p

- (void)viewDidLoad
{
    ...
    CGRect r = self.navigationController.navigationBar.bounds;
    UILabel *titleView = [[UILabel alloc] initWithFrame:r];
    titleView.autoresizingMask = 
      UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    titleView.textAlignment = NSTextAlignmentCenter;
    titleView.font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
    titleView.text = self.title;
    titleView.userInteractionEnabled = YES;
    UITapGestureRecognizer *tgr =
      [[UITapGestureRecognizer alloc] initWithTarget:self
                                              action:@selector(titleViewWasTapped:)];
    tgr.numberOfTapsRequired = 1;
    tgr.numberOfTouchesRequired = 1;
    tgr.delegate = self;
    [titleView addGestureRecognizer:tgr];
    [tgr release];
    self.navigationItem.titleView = titleView;
    [titleView release];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
{
    // This method is needed because the navigation bar with back
    // buttons will swallow touch events
    return (gestureRecognizer.view == self.navigationItem.titleView);
}

然后像往常一样使用手势识别器

- (void)titleViewWasTapped:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateRecognized) {
        return;
    }
    ...
}