11

更新:虽然我仍然想解决这个问题,但我最终切换到animateWithDuration:delay:options:animations:completion:并且效果更好。它缺少弹簧提供的末端那种美妙的“反弹”,但至少它是可控的。


我正在尝试为 iOS 创建一个漂亮的手势驱动 UI,但是在获取这些值以产生一个不错的自然感觉应用程序时遇到了一些困难。

我使用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;
    }];
}
4

3 回答 3

13

在寻找相关问题的解决方案时遇到了这篇文章。问题是,当需要时,您正在传递来自 的速度UIPanGestureRecognizer,以点/秒为单位- animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion……稍微奇怪的值:

初始弹簧速度。为了平滑地开始动画,将此值与视图的速度相匹配,因为它在附加之前。值 1 对应于一秒内遍历的总动画距离。例如,如果总动画距离为 200 点,并且您希望动画的开始与 100 pt/s 的视图速度相匹配,则使用值 0.5。

动画方法需要每秒“距离”的速度,而不是每秒点数。因此,您应该传入的值是(来自手势识别器的速度)/(动画期间行进的总距离)。

也就是说,这正是我正在做的事情,并且在手势识别器移动它和动画开始之间仍然存在轻微但明显的“打嗝”。也就是说,它应该仍然比你以前的工作好很多。

于 2013-12-18T10:09:37.913 回答
3

首先,您需要计算动画必须处理的剩余距离。当你有增量距离时,你可以继续计算速度,如下所示:

CGFloat springVelocity = fabs(gestureRecognizerVelocity / distanceToAnimate);

对于干净的速度传输,您必须使用UIViewAnimationOptionCurveLinear.

于 2015-06-24T13:55:32.490 回答
0

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

一点上下文:我需要在 UIView 的一侧使用 panGestureRecognizer 来调整它的大小。

  • 如果 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)
 }

希望有帮助。

于 2017-08-31T08:06:37.323 回答