Разрешить UIScrollView и его подзаголовкам отвечать на прикосновение
Я хочу, чтобы и мой UIScrollView, и его подпрограммы получили все события касания внутри подвью. Каждый может ответить по-своему.
В качестве альтернативы, если жесты нажатия были переадресованы в подпункты, все будет хорошо.
Многие люди борются в этой общей области. Вот несколько из многих связанных вопросов:
Как кражи UIScrollView касаются его подзонов
Как украсть штрихи от UIScrollView?
Как отменить прокрутку в UIScrollView
Кстати, если я переопределяю hitTest: withEvent: в представлении прокрутки я вижу касания до тех пор, пока userInteractionEnabled равен YES. Но это действительно не решает мою проблему, потому что:
1) В этот момент я не знаю, если это кран или нет.
2) Иногда мне нужно установить userInteractionEnabled в НЕТ.
РЕДАКТИРОВАТЬ: Чтобы прояснить, да, я хочу обрабатывать краны по-разному от сковородок. Отводы должны обрабатываться подзонами. Кастрюли могут обрабатываться в виде прокрутки обычным способом.
Ответы
Ответ 1
Во-первых, отказ от ответственности. Если вы установите userInteractionEnabled
на NO
на UIScrollView
, сенсорные события не будут переданы в подзоны. Насколько мне известно, нет никакого способа обойти это с одним исключением: перехватить сенсорные события над надстройкой UIScrollView
и, в частности, передать эти события в подпункты UIScrollView
. Если честно, я не знаю, почему вы хотели бы это сделать. Если вы хотите отключить определенные функции UIScrollView (например,... прокрутку), вы можете сделать это достаточно легко, не отключая UserInteraction.
Если я понимаю ваш вопрос, вам нужны события перехода, которые будут обрабатываться с помощью UIScrollView и, переданных в subviews? В любом случае (независимо от жестов), я думаю, что вы ищете метод протокола gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: в протоколе UIGestureRecognizerDelegate
, В ваших подзонах, независимо от того, какие у вас распознаватели жестов, установите делегат (возможно, любой класс устанавливает UIGestureReconginzer
в первую очередь) на распознаватель жестов. Переопределите вышеуказанный метод и верните YES
. Теперь этот жест будет распознан вместе с любыми другими распознавателями, которые могли бы "украсть" жест (в вашем случае, краном). Используя этот метод, вы можете даже точно настроить свой код, чтобы отправлять только отдельные виды жестов в подзаголовки или отправлять жест только в определенных ситуациях. Это дает вам большой контроль. Просто не забудьте прочитать о методе, особенно в этой части:
Этот метод вызывается при распознавании жестов посредством либо gestureRecognizer, либо другойGestureRecognizer блокирует другой распознаватель жестов признает его жест. Обратите внимание, что возврат ДА гарантирует возможность одновременного распознавания; возврат НЕТ, с другой стороны, не гарантируется одновременное распознавание, потому что другой распознаватель жестов делегат может вернуть ДА.
Конечно, есть оговорка: это касается только распознавателей жестов. Таким образом, у вас могут возникнуть проблемы, если вы пытаетесь использовать touchesBegan:
, touchesEnded
и т.д. Для обработки штрихов. Разумеется, вы можете использовать hitTest:
для отправки сырых событий касания к подзонам, но почему? Зачем обрабатывать события с помощью этих методов в UIView
, когда вы можете прикрепить UIGestureRecognizer
к представлению и получить все эти функции бесплатно? Если вам нужны штрихи, обработанные таким образом, что ни один стандартный UIGestureRecognizer
не может предоставить, подкласс UIGestureRecognizer
и обрабатывать штрихи там. Таким образом, вы получаете все функциональные возможности UIGestureRecognizer
, а также свою собственную сенсорную обработку. Я действительно думаю, что Apple предназначалась для UIGestureRecognizer
для замены большинства (если не всех) настраиваемого кода обработки, который разработчики используют на UIView
. Это позволяет повторно использовать код, и с ним легче справляться, если смягчить, какой код обрабатывает то, что касается события.
Ответ 2
Я не знаю, может ли это помочь вам, но у меня была аналогичная проблема, когда я хотел, чтобы scrollview обрабатывал двойное нажатие, но перетащил одно нажатие на subviews. Вот код, используемый в CustomScrollView
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
// Coordinates
CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]];
// One tap, forward
if(touch.tapCount == 1){
// for each subview
for(UIView* overlayView in self.subviews){
// Forward to my subclasss only
if([overlayView isKindOfClass:[OverlayView class]]){
// translate coordinate
CGPoint newPoint = [touch locationInView:overlayView];
//NSLog(@"%@",NSStringFromCGPoint(newPoint));
BOOL isInside = [overlayView pointInside:newPoint withEvent:event];
//if subview is hit
if(isInside){
Forwarding
[overlayView touchesEnded:touches withEvent:event];
break;
}
}
}
}
// double tap : handle zoom
else if(touch.tapCount == 2){
if(self.zoomScale == self.maximumZoomScale){
[self setZoomScale:[self minimumZoomScale] animated:YES];
} else {
CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point];
[self zoomToRect:zoomRect animated:YES];
}
[self setNeedsDisplay];
}
}
Конечно, эффективный код должен быть изменен, но на этом этапе у вас должна быть вся информация, необходимая для принятия решения о необходимости переадресации события. Возможно, вам понадобится реализовать это в другом методе как касаниеMoved: withEvent:.
Надеюсь, это поможет.
Ответ 3
У меня была такая же проблема, но с прокруткой, которая находилась внутри UIPageViewController
, поэтому ее нужно было обрабатывать несколько иначе.
Изменив свойство cancelsTouchesInView
на false для каждого распознавателя на UIScrollView
, я смог получить штрихи к кнопкам внутри UIPageViewController
.
Я сделал это, добавив этот код в viewDidLoad
:
guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else {
print("No gesture recognizers on scrollview.")
return
}
for recognizer in recognizers {
recognizer.cancelsTouchesInView = false
}
Ответ 4
Если вам нужно различать прикосновение и прокрутку, вы можете проверить, были ли тросы перемещены. Если это касание, то touchHasBeenMoved не будет вызываться, тогда вы можете предположить, что это прикосновение.
В этот момент вы можете установить логическое значение, чтобы указать, если movnent accoured и установить это Boolean как условие в других ваших методах.
Я нахожусь в дороге, но если это вам понадобится, я смогу объяснить лучше позже.
Ответ 5
Хакерный способ достижения вашей цели - не 100% точный - заключается в подклассе UIWindow и переопределении события - (void) sendEvent: (UIEvent *);
Быстрый пример:
в заголовке SecondResponderWindow.h
//SecondResponderWindow.h
@protocol SecondResponderWindowDelegate
- (void)userTouchBegan:(id)tapPoint onView:(UIView*)aView;
- (void)userTouchMoved:(id)tapPoint onView:(UIView*)aView;
- (void)userTouchEnded:(id)tapPoint onView:(UIView*)aView;
@end
@interface SecondResponderWindow : UIWindow
@property (nonatomic, retain) UIView *viewToObserve;
@property (nonatomic, assign) id <SecondResponderWindowDelegate> controllerThatObserves;
@end
в SecondResponderWindow.m
//SecondResponderWindow.m
- (void)forwardTouchBegan:(id)touch onView:(UIView*)aView {
[controllerThatObserves userTouchBegan:touch onView:aView];
}
- (void)forwardTouchMoved:(id)touch onView:(UIView*)aView {
[controllerThatObserves userTouchMoved:touch onView:aView];
}
- (void)forwardTouchEnded:(id)touch onView:(UIView*)aView {
[controllerThatObserves userTouchEnded:touch onView:aView];
}
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
if (viewToObserve == nil || controllerThatObserves == nil) return;
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
if ([touch.view isDescendantOfView:viewToObserve] == NO) return;
CGPoint tapPoint = [touch locationInView:viewToObserve];
NSValue *pointValue = [NSValue valueWithCGPoint:tapPoint];
if (touch.phase == UITouchPhaseBegan)
[self forwardTouchBegan:pointValue onView:touch.view];
else if (touch.phase == UITouchPhaseMoved)
[self forwardTouchMoved:pointValue onView:touch.view];
else if (touch.phase == UITouchPhaseEnded)
[self forwardTouchEnded:pointValue onView:touch.view];
else if (touch.phase == UITouchPhaseCancelled)
[self forwardTouchEnded:pointValue onView:touch.view];
}
Это не 100% соответствует тому, что вы ожидали, потому что ваш второй ответчик не обрабатывает событие касания изначально через -touchDidBegin: или так, и ему нужно реализовать SecondResponderWindowDelegate. Однако этот хак позволяет обрабатывать события касания на дополнительных респондентах.
Этот метод вдохновлен и расширен от MITHIN KUMAR TapDetectingWindow