UIView анимация с UIPanGestureRecognizer скорость слишком быстро (не замедляется)

Обновление: хотя мне все же хотелось бы это решить, я перешел на animateWithDuration:delay:options:animations:completion: и он работает намного приятнее. В конце этого отсутствует хороший «отскок» в конце, который дает весна, но, по крайней мере, это контролируемо.


Я пытаюсь создать хороший пользовательский интерфейс, ориентированный на жесты, для iOS, но я сталкиваюсь с некоторыми трудностями, получая значения, чтобы привести к приятному приложению естественного ощущения.

Я использую animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion: потому что мне нравится оживленная весенняя анимация. Я инициализирую аргумент velocity со скоростью, заданной распознавателем жестов в завершенном состоянии. Проблема в том, что, если я буду достаточно быстро капать и отпустить, скорость в тысячах, и мой взгляд заканчивается, пролетая прямо с экрана, а затем отскакивает назад и вперед с такой головокружительной мести.

Я даже корректирую продолжительность анимации относительно количества расстояния, которое нужно перемещать, поэтому, если требуется только несколько пикселей, анимация займет меньше времени. Это, однако, не решило проблему. Он все еще заканчивается.

То, что я хочу сделать, – это представление должно начинаться с любой скорости, в которую пользователь перетаскивает его, но он должен быстро замедляться при достижении целевой точки и только слегка отскакивать в конце (так же, как если бы скорость была чем-то разумным ).

Интересно, правильно ли я использую этот метод или значения. Вот какой код, чтобы показать, что я делаю. Любая помощь будет оценена!

 - (void)handlePanGesture:(UIPanGestureRecognizer*)gesture { CGPoint offset = [gesture translationInView:self.view]; CGPoint velocity = [gesture velocityInView:self.view]; NSLog(@"pan gesture state: %d, offset: %f velocity: %f", gesture.state, offset.x, velocity.x); static CGFloat initialX = 0; switch ( gesture.state ) { case UIGestureRecognizerStateBegan: { initialX = self.blurView.x; break; } case UIGestureRecognizerStateChanged: { self.blurView.x = initialX + offset.x; break; } default: case UIGestureRecognizerStateCancelled: case UIGestureRecognizerStateEnded: { if ( velocity.x > 0 ) [self openMenuWithVelocity:velocity.x]; else [self closeMenuWithVelocity:velocity.x]; break; } } } - (void)openMenuWithVelocity:(CGFloat)velocity { if ( velocity < 0 ) velocity = 1.5f; CGFloat distance = -40 - self.blurView.x; CGFloat distanceRatio = distance / 260; NSLog(@"distance: %f ratio: %f", distance, distanceRatio); [UIView animateWithDuration:(0.9f * distanceRatio) delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:velocity options:UIViewAnimationOptionBeginFromCurrentState animations:^{ self.blurView.x = -40; } completion:^(BOOL finished) { self.isMenuOpen = YES; }]; } 

Нашел этот пост, ища решение проблемы. Проблема в том, что вы передаете скорость от UIPanGestureRecognizer , которая находится в точках / - animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion , когда - animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion хочет … немного более странное значение:

Начальная скорость пружины. Для плавного начала анимации сопоставьте это значение со скоростью представления, как это было до вложения. Значение 1 соответствует общему расстоянию анимации, пройденному за одну секунду. Например, если общее расстояние анимации составляет 200 точек, и вы хотите, чтобы начало анимации соответствовало скорости просмотра 100 пт / с, используйте значение 0,5.

Метод анимации требует скорости в «расстояниях» в секунду, а не в секундах. Итак, значение, которое вы должны пройти, – это скорость от распознавателя жестов / (общее расстояние, пройденное во время анимации).

Тем не менее, это именно то, что я делаю, и все еще есть небольшая, но заметная «икота» между тем, когда движок распознавания жестов перемещает его, и когда анимация поднимается. Тем не менее, он все равно должен работать намного лучше, чем у вас раньше. 😃

Сначала вам нужно рассчитать оставшееся расстояние, которое анимация должна будет позаботиться. Когда у вас есть дельта-расстояние, вы можете перейти к вычислению скорости следующим образом:

 CGFloat springVelocity = fabs(gestureRecognizerVelocity / distanceToAnimate); 

Для чистой передачи скорости вы должны использовать UIViewAnimationOptionCurveLinear .

У меня была немного другая потребность, но мой код может помочь, а именно вычисление скорости, основанное на (pan velocity / pan translation).

Немного контекста: мне нужно было использовать panGestureRecognizer на стороне UIView, чтобы изменить его размер.

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

Пейзаж

Портрет

Это то, что я использовал в IBAction для UIPanGestureRecognizer:

 var velocity: CGFloat = 1 switch gesture.state { case .changed: // Adjust the resizableView size according to the gesture translation let translation = gesture.translation(in: resizableView) let panVelocity = gesture.velocity(in: resizableView) if isPortrait { // defined previously in the class based on UIDevice orientation let newHeight = resizableViewHeightConstraint.constant + (-translation.y) resizableViewHeightConstraint.constant = newHeight // UIView animation initialSpringVelocity requires a velocity based on the total distance traveled during the animation velocity = -panVelocity.y / -panTranslation.y } else { // Landscape let newWidth = resizableViewWidthConstraint.constant + (translation.x) // Limit the resizing to half the width on the left and the full width on the right resizableViewWidthConstraint.constant = min(max(resizableViewInitialSize, newWidth), self.view.bounds.width) // UIView animation initialSpringVelocity requires a velocity based on the total distance traveled during the animation velocity = panVelocity.x / panTranslation.x } UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: velocity, options: [.curveEaseInOut], animations: { self.view.layoutIfNeeded() }, completion: nil) // Reset translation gesture.setTranslation(CGPoint.zero, in: resizableView) } 

Надеюсь, это поможет.

Interesting Posts
Давайте будем гением компьютера.