Ответ 1
UIGestureRecognizerDelegate имеет метод под названием "gestureRecognizer: shouldReceiveTouch". Если вы можете указать, является ли сенсорный вид кнопкой, просто верните "НЕТ", в противном случае верните "ДА", и вы хорошо пойдете.
Вопрос в первую очередь заключался в следующем:
Когда у вас есть 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". Их события щелчка переопределяются распознавателем жестов
Мой вопрос:
Как я могу получить приятную функцию, которую мой NavigationBar можно щелкнуть, но все еще можно использовать кнопки "Назад" в моем приложении?
Итак, либо найдите другое решение, которое не отменяет кнопку "Назад" или не найдет решение, чтобы вернуть обратно кнопку назад:)
UIGestureRecognizerDelegate имеет метод под названием "gestureRecognizer: 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;
}
EDIT:
Состязание по углу кнопки "Назад" по-прежнему перезаписывается, поэтому вы создали этот код pointInside:withEvent
:
CGRectContainsPoint((CGRect){ .origin = subview.frame.origin, .size = CGSizeMake(subview.frame.size.width + 16, subview.frame.size.height)}, point)
Xamarin.iOS не предоставляет оболочки С# для классов Objective-C в частном API, поэтому чистая проверка подклассов, предложенная @ben-flynn выше, не будет работать здесь.
Несколько хакерских обходных пути заключается в проверке поля Description
видов:
navigationTitleTap = new UITapGestureRecognizer (tap => DidTapNavigationTitle());
navigationTitleTap.ShouldReceiveTouch = (recognizer, touch) =>
touch.View.Subviews.Any(sv =>
// Is this the NavigationBar 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;
}
...
}