Вперед касается UIScrollView
У меня есть два контроллера вида. Просмотр контроллера A имеет UIScrollView
и представляет собой контроллер представления B. Презентация интерактивна и управляется с помощью scrollView.contentOffset
.
Я хочу интегрировать интерактивный переход перехода: при панорамировании, ViewController B должен быть отклонен в интерактивном режиме. Переключение интерактивного отклонения также должно контролировать прокруткуViewController A.
Моя первая попытка заключалась в использовании UIPanGestureRecognizer
и установке scrollView.contentOffset
в соответствии с запланированным расстоянием. Это работает, однако, когда жест панорамы закончился, смещение scrollView должно быть анимировано в конечную позицию. Использование -[UIScrollView setContentOffset:animated:
не является хорошим решением, поскольку оно использует линейную анимацию, не учитывает текущую скорость панорамирования и не замедляется.
Итак, я подумал, что можно было бы передавать события касания с моего распознавателя жестов в виде прокрутки. Это должно дать нам все приятное поведение анимации прокрутки.
Я попытался переопределить методы -touchesBegan/Moved/Ended/Cancelled withEvent:
в моем подклассе UIPanGestureRecognizer
следующим образом:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[scrollView touchesBegan:touches withEvent:event];
[scrollView.panGestureRecognizer touchesBegan:touches withEvent:event];
[super touchesBegan:touches withEvent:event];
}
Но, по-видимому, что-то блокирует просмотр прокрутки из входа в режим tracking
. (Он идет на dragging = YES
, но об этом.) Я проверил scrollView userInteractionEnabled
, а не скрыт и добавил в иерархию представления.
Итак, как я могу переслать свои события касания на UIScrollView
?
Ответы
Ответ 1
После прочтения интересного ответа, описывающего поток событий UIScrollView
, я пришел к выводу, что попытка "удаленного управления" вид прокрутки из распознавателя жестов, вероятно, очень трудно достичь, потому что прикосновения мутируются, когда их направляют к представлениям и распознающим жестом. Поскольку UITouch
не соответствует NSCopying
, мы также не можем клонировать события касания, чтобы отправить их позже в немодифицированном состоянии.
Пока я не решал проблему, о которой я просил, я нашел обходное решение, чтобы выполнить то, что мне нужно. Я просто добавил представление прокрутки для просмотра контроллера B и синхронизировал его с просмотром прокрутки VC A (который добавляется в иерархию представления при вертикальной прокрутке):
// delegate of VC B scrollView
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
scrollViewA.contentOffset = scrollView.contentOffset;
}
Спасибо Фридриху Маргграфу, который придумал идею.
Ответ 2
Попробуйте подклассифицировать UIScrollView
и переопределить hitTest:withEvent:
, чтобы UIScrollView
забирал штрихи за пределами своих границ. Затем вы получите все приятные анимации UIScrollView
бесплатно. Что-то вроде этого:
@interface MagicScrollView : UIScrollView
@end
@implementation MagicScrollView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// Intercept touches 100pt outside this view bounds on all sides. Customize this for the touch area you want to intercept
if (CGRectContainsPoint(CGRectInset(self.bounds, -100, -100), point)) {
return self;
}
return nil;
}
@end
Или в Свифт (спасибо Эдвину Вермееру!)
class MagicScrollView: UIScrollView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if CGRectContainsPoint(CGRectInset(self.bounds, -100, -100), point) {
return self
}
return nil
}
}
Вам также может потребоваться переопределить pointInside:withEvent:
в супервизии UIScrollView
, в зависимости от вашего макета.
См. следующий вопрос для получения дополнительной информации: взаимодействие за пределами uiview
Ответ 3
Возможно, вы можете попробовать использовать этот метод в делегате UIPanGesture:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
Я не знаю, как это будет реагировать, поскольку ваши взгляды являются модальными. Но, возможно, это может преуспеть, как без него, без непосредственного касания с beginTouch и т.д., Которые являются болью в заднице.
Скажите, если это поможет?
Ответ 4
В качестве дополнения к решению @johnboiles. Когда вы поймаете весь прямоугольник, то касания элементов внутри прокрутки будут пропущены. Вы можете добавить дополнительные сенсорные области, и для обычного прокрутки просто передайте его на super.hitTest, например:
class MagicScrollView: UIScrollView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if CGRectContainsPoint(CGRect(x: self.bounds.origin.x - 30, y: self.bounds.origin.y, width: 30, height: self.bounds.size.height), point) {
return self
}
if CGRectContainsPoint(CGRect(x: self.bounds.origin.x + self.bounds.size.width, y: self.bounds.origin.y, width: 30, height: self.bounds.size.height), point) {
return self
}
return super.hitTest(point, withEvent: event)
}
}
Ответ 5
У меня есть scrollview внутри супервизора, чтобы иметь левую и правую границы. Чтобы прокрутить эти границы, это был мой подход:
class CustomScrollView: UIScrollView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let view = super.hitTest(point, withEvent: event) {
return view
}
guard let superview = superview else {
return nil
}
return superview.hitTest(superview.convertPoint(point, fromView: self), withEvent: event)
}
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
if super.pointInside(point, withEvent: event) {
return true
}
guard let superview = superview else {
return false
}
return superview.pointInside(superview.convertPoint(point, fromView: self), withEvent: event)
}
}
Отлично работает