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



第一个问题是:

当你有一个tableView如何实现用户可以点击导航条滚动到顶部

解决方案:

- (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];
}

效果很好!

但是Drarok指出了一个问题:

这种方法只有在你没有后退按钮或rightBarButtonItem的情况下才可行。它们的点击事件被手势识别器

覆盖。

我的问题:

我怎么能有很好的功能,我的导航栏是可点击的,但仍然能够使用后退按钮在我的应用程序?

所以要么找到一个不覆盖后退按钮的不同解决方案,要么找到一个让后退按钮重新工作的解决方案:)

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

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

请注意,导航按钮的类型是UINavigationButton,它没有公开,因此子类检查。

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

UIGestureRecognizerDelegate有一个叫做"gestureRecognizer:shouldReceiveTouch"的方法。如果你能指出触摸视图是否是按钮,只要让它返回"NO"否则返回"YES"就可以了

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的动作也被执行。如果你不介意的话,这是我能想到的最简单的解决方法。

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)

Xamarin。iOS没有在私有API中为Objective-C类公开c#包装器,所以上面@ben-flynn建议的简洁的子类检查在这里不起作用。

一个比较hack的解决方法是检查视图的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>在查找视图层次结构中的特定类型时很方便。

这对我有用,这是基于Stavash的答案。我使用手势识别器的view属性在委托方法中返回YES/NO。

这是一个旧的应用,所以很明显这不是ARC,没有使用新的布局和nsattribuedstring。我留给你: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;
    }
    ...
}

最新更新