【问题标题】:Click events in UINavigationBar overridden by the gesture recognizerUINavigationBar 中的单击事件被手势识别器覆盖
【发布时间】:2011-10-14 20:24:56
【问题描述】:

首先的问题是:

当您有一个 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];
}

这就像一个魅力!

但是 Drarok 指出了一个问题:

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

我的问题:

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

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

【问题讨论】:

    标签: ios uitableview uinavigationbar uigesturerecognizer


    【解决方案1】:

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

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

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

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

    【讨论】:

    • UIControl 类的双重检查非常聪明。谢谢!
    • pre iOS6 的超优雅解决方案。
    • 似乎不适用于后退按钮。当按下后退按钮时 [[touch view] class] 也是 UINavigationBar
    【解决方案2】:

    UIGestureRecognizerDelegate 有一个名为“gestureRecognizer:shouldReceiveTouch”的方法。如果您能够指出触摸的视图是否是按钮,只需使其返回“NO”,否则返回“YES”,您就可以开始了。

    【讨论】:

    • 嗯,我找到了方法和一切,并让它开火。但是传递给该方法的 UITouch 为零:-/我从未使用过 UITouch 或 UIGestureRecognizers。如何获取触发委托方法的触摸位置?
    • 您可以尝试“locationInView”,将其传递给导航栏的视图。如果一切设置正确(确保已分配委托),您应该在给定视图中收到触摸的相对 CGPoint。
    • Funny.. 这正是我首先尝试的 :) 但得到了 EXC_BAD_ACCESS,在检查赋予委托方法的 UITouch 后,我发现它是 nil :-/ 我真的不明白为什么,因为我认为没有 UITouch 就不会调用该方法?
    • 没关系我在某个地方犯了一些错误..让我再试一次..对不起
    • 嗨,使用“locationInView”返回 x 和 y 坐标中的触摸点。但是我的后栏按钮项目的宽度不断变化。因为,我根据我的表格视图控制器中单击的单元格设置了后栏按钮的标题值。那么,如何识别后退栏按钮项是否被点击呢?甚至我也试图通过[touch view].class 获取类名。但它总是为titleView单击和返回栏按钮单击事件返回“UINavigaionBar”类
    【解决方案3】:

    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 的动作也会被执行。如果这不打扰您,那将是我能想到的最简单的解决方案。

    【讨论】:

    • 优秀的答案!很简单!谢谢。
    【解决方案4】:

    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)
    

    【讨论】:

      【解决方案5】:

      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&lt;T&gt; 很方便,但在查找视图层次结构中的某些类型时很方便。

      【讨论】:

        【解决方案6】:

        这对我有用,它基于 Stavash 的回答。我使用手势识别器的 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;
            }
            ...
        }
        

        【讨论】:

          猜你喜欢
          • 2012-06-25
          • 2018-12-31
          • 1970-01-01
          • 1970-01-01
          • 2019-11-29
          • 2021-09-27
          • 1970-01-01
          • 2014-03-01
          • 1970-01-01
          相关资源
          最近更新 更多