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

发布于 2024-12-09 22:49:50 字数 774 浏览 7 评论 0原文

首先的问题是:

当你有一个 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 时,这种方法才可行。他们的点击事件被手势识别器覆盖

我的问题:

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

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

The question in the first place was:

When you have a tableView how to implement that the user can tap the NavigationBar to scroll all the way to the top.

Solution:

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

Which works like a charm!

But Drarok pointed out an issue:

This approach is only viable if you don't have a back button, or rightBarButtonItem. Their click events are overridden by the gesture recognizer

My question:

How can I have the nice feature that my NavigationBar is clickable but still be able to use the back buttons in my app?

So either find a different solution that doesn't override the back button or find a solution to get the back button back to working again :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

你的背包 2024-12-16 22:49:51

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)

iOS7 version:

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

EDIT:

Someting on the corner of back button gesture is still overwritten, so you this code insted of pointInside:withEvent:

CGRectContainsPoint((CGRect){ .origin = subview.frame.origin, .size = CGSizeMake(subview.frame.size.width + 16, subview.frame.size.height)}, point)
咽泪装欢 2024-12-16 22:49:51

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 很方便。

Xamarin.iOS does not expose C# wrappers for Objective-C classes in the private API, so the neat subclass checking suggested by @ben-flynn above won't work here.

A somewhat hackish workaround is to check the Description field of the views:

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);

The Linq collection filter .OfType<T> is convenient though when fishing for certain types in the view hierarchy.

几度春秋 2024-12-16 22:49:51

这对我有用,它基于 Stavash 的答案。我使用手势识别器的视图属性在委托方法中返回“是”/“否”。

这是一个旧的应用程序,所以显然这不是 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;
    }
    ...
}

This worked for me, It is based on Stavash answer. I use the view property of the gesture recognizer to return YES/NO in the delegate method.

It is an old app so obviously this is not ARC, does not use new layout stuff nor NSAttributed strings. I leave that to you :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);
}

Then you use the gesture recogniser as usual

- (void)titleViewWasTapped:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateRecognized) {
        return;
    }
    ...
}
深爱不及久伴 2024-12-16 22:49:50

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

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

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

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

Rather than using location view, I solved this by checking the class of the UITouch.

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

Note that the nav buttons are of type UINavigationButton which is not exposed, hence the subclass checking.

This method goes in the class you designate as the delegate of the gesture recognizer. If you're just getting started with gesture recognizers, note that the delegate is set separately from the target.

不醒的梦 2024-12-16 22:49:50

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

UIGestureRecognizerDelegate has a method called "gestureRecognizer:shouldReceiveTouch". If you are able to point out if the touch's view is the button, just make it return "NO", otherwise return "YES" and you're good to go.

朮生 2024-12-16 22:49:50

UIGestureRecognizer 还有一个属性 @property(nonatomic) BOOL cancelsTouchesInView。来自文档:一个布尔值,影响识别手势时是否将触摸传递到视图。

因此,如果您只是这样做,

tapRecon.cancelsTouchesInView = NO;

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

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

UIGestureRecognizer also has an attribute @property(nonatomic) BOOL cancelsTouchesInView. From the documentation: A Boolean value affecting whether touches are delivered to a view when a gesture is recognized.

So if you simply do

tapRecon.cancelsTouchesInView = NO;

this might be an even simpler solution, depending on your use case. This is how I do it in my app.

When pressing a button in the navigation bar though, its action is executed (as desired), but the UIGestureRecognizer's action is executed as well. If that doesn't bother you, then that would be the simplest solution I could think of.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文