Подтвердить что ты не робот

Вперед касается 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?

4b9b3361

Ответ 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)
  }
}

Отлично работает