События щелчка в UINavigationBar, переопределенные распознавателем жестов

Вопрос в первую очередь был:

Когда у вас есть 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. Их события щелчка переопределяются распознавателем жестов

Мой вопрос:

Как я могу получить приятную особенность, заключающуюся в том, что моя панель навигации кликабельна, но все еще может использовать кнопки "Назад" в моем приложении?

Так что либо найдите другое решение, которое не отменяет кнопку возврата, либо найдите решение, чтобы кнопка возврата снова заработала:)

6 ответов

Решение

UIGestureRecognizerDelegate имеет метод, который называется "gestRecognizer:shouldReceiveTouch". Если вы можете указать, является ли сенсорный экран кнопкой, просто верните "НЕТ", в противном случае верните "ДА", и все готово.

Вместо того, чтобы использовать вид местоположения, я решил это, проверив класс UITouch.

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

Обратите внимание, что кнопки навигации имеют тип UINavigationButton который не выставлен, следовательно проверка подкласса.

Этот метод входит в класс, который вы определяете как делегат распознавателя жестов. Если вы только начинаете работать с распознавателями жестов, обратите внимание, что делегат устанавливается отдельно от цели.

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 не предоставляет оболочки C# для классов Objective-C в закрытом API, поэтому аккуратная проверка подклассов, предложенная @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> Это удобно при ловле для определенных типов в иерархии представлений.

Это сработало для меня, это основано на ответе Ставаша. Я использую свойство 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;
    }
    ...
}
Другие вопросы по тегам