UIView动画和UIPanGestureRecognizer速度过快(不减速)

11

更新:虽然我仍然想解决这个问题,但最终我切换到了animateWithDuration:delay:options:animations:completion:,它的效果更好。它缺少弹簧动画提供的漂亮的“反弹”效果,但至少是可控的。

- (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;
    }];
}
3个回答

13
在寻找解决相关问题的方案时,发现了这篇文章。问题是,你正在传入来自UIPanGestureRecognizer的速度,它以每秒点数为单位,而-animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion需要一个稍微奇怪一些的值:

初始弹簧速度。为了使动画开始平滑,将该值与视图附加前的速度相匹配。 值1对应于在一秒钟内穿过的总动画距离。例如,如果总动画距离为200个点,并且您希望动画的开头匹配100pt/s的视图速度,请使用0.5的值。

动画方法需要“距离”每秒的速度,而不是每秒的点数。因此,您应该传递的值是(手势识别器的速度)/(动画期间行进的总距离)。
也就是说,这正是我所做的,但是在手势识别器移动它的时候,还是会有轻微但明显的“抽搐”感觉,直到动画开始。尽管如此,它仍比之前的方法要好得多。

嘿,你最终搞清楚如何正确使用初始弹簧速度了吗? - bogardon
没有!最终还是带着一个框架的小问题发布了... - Cocoatype

3

首先,您需要计算动画剩余要处理的距离。当您有了增量距离后,可以按照以下方式计算速度:

CGFloat springVelocity = fabs(gestureRecognizerVelocity / distanceToAnimate);

为了进行干净的速度转换,您必须使用 UIViewAnimationOptionCurveLinear

0

我的需求略有不同,但我的代码可能会有所帮助,即基于(平移速度/平移距离)的速度计算。

一些背景:我需要在UIView的侧面使用panGestureRecognizer来调整其大小。

  • 如果iPad处于纵向模式,则视图附加在左侧、底部和右侧,并且我在视图的顶部边界上拖动以调整其大小。
  • 如果iPad处于横向模式,则视图附加在左侧、顶部和底部,并且我在右侧边框上拖动以调整其大小。

Landscape

Portrait

这是我在UIPanGestureRecognizer的IBAction中使用的:

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)
 }

希望这有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接