ios 基于 UIPanGestureRecognizer 速度的 UIView 动画
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8121301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UIView animation based on UIPanGestureRecognizer velocity
提问by Neigaard
I would like to be able to move a subview on and off the screen much like you browse between images in the iPhone's build in Photos app, so if the subview is more than 1/2 off screen when I let go with my finger it must animate off screen, but it must also support swipe so if the swipe/pan velocity is high enough it must animate off screen even though is might be less than 1/2 off screen.
我希望能够在屏幕上和屏幕外移动子视图,就像您在 iPhone 内置照片应用程序中的图像之间浏览一样,所以如果当我用手指松开时子视图离屏幕超过 1/2,它必须屏幕外动画,但它也必须支持滑动,所以如果滑动/平移速度足够高,它必须在屏幕外动画,即使可能小于屏幕外的 1/2。
My idea was to use UIPanGestureRecognizer and then test on the velocity. This works, but how do I set a correct animation duration for the moving of the UIView based on the view's current location and velocity of the pan so that it seems smooth? If I set a fixed value, the animation either starts to slow or to fast compared to my fingers swipe speed.
我的想法是使用 UIPanGestureRecognizer 然后测试速度。这有效,但是如何根据视图的当前位置和平移速度为 UIView 的移动设置正确的动画持续时间,使其看起来平滑?如果我设置了一个固定值,与我的手指滑动速度相比,动画要么开始变慢,要么变快。
回答by Dennis
The docs say
文档说
The velocity of the pan gesture, which is expressed in points per second. The velocity is broken into horizontal and vertical components.
平移手势的速度,以每秒点数表示。速度分为水平分量和垂直分量。
So I'd say, given you want to move your view xPoints
(measured in pt) to let it go off-screen, you could calculate the duration for that movement like so:
所以我想说,如果你想移动你的视图xPoints
(以 pt 为单位)让它离开屏幕,你可以像这样计算该移动的持续时间:
CGFloat xPoints = 320.0;
CGFloat velocityX = [panRecognizer velocityInView:aView].x;
NSTimeInterval duration = xPoints / velocityX;
CGPoint offScreenCenter = moveView.center;
offScreenCenter.x += xPoints;
[UIView animateWithDuration:duration animations:^{
moveView.center = offScreenCenter;
}];
You might want to use + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
instead though and try out different UIViewAnimationOptions
.
不过,您可能想+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
改用并尝试不同的UIViewAnimationOptions
.
回答by Rob
An observation on relying upon the velocity in UIPanGestureRecognizer
: I don't know about your experience, but I found the system generated velocity on the simulator to be not terribly useful. (It's ok on the device, but problematic on simulator.)
对依赖速度的观察UIPanGestureRecognizer
:我不知道您的经验,但我发现系统在模拟器上生成的速度并不是非常有用。(在设备上没问题,但在模拟器上有问题。)
If you pan quickly across and abruptly stop, wait, and only then end the gesture (e.g. user starts a swipe, realizes that this wasn't what they wanted, so they stop and then release their finger), the velocity reported by velocityInView:
at the UIGestureRecognizerStateEnded
state when your finger was released seems to be the fast speed before I stopped and waited, whereas the right velocity in this example would be zero (or near zero). In short, the reported velocity is that which it was right before the end of the pan, but not the speed at the end of the pan itself.
如果您快速平移并突然停止,等待,然后才结束手势(例如,用户开始滑动,意识到这不是他们想要的,所以他们停下来然后松开手指),velocityInView:
在UIGestureRecognizerStateEnded
松开手指时的状态似乎是我停下来等待之前的快速速度,而本例中的正确速度为零(或接近零)。简而言之,报告的速度是在平底锅结束之前的速度,而不是平底锅结束时的速度。
I ended up calculating the velocity myself, manually. (It seems silly that this is necessary, but I didn't see any way around it if I really wanted to get the final speed of the pan.) Bottom line, when the state is UIGestureRecognizerStateChanged
I keep track of the current and previous translationInView
CGPoint as well as the time, and then using those values when I was in the UIGestureRecognizerStateEnded
to calculate the actual final velocity. It works pretty well.
我最终自己手动计算速度。(这似乎很愚蠢,但如果我真的想获得平底锅的最终速度,我没有看到任何解决方法。)底线,当状态为时,UIGestureRecognizerStateChanged
我将当前和以前的translationInView
CGPoint跟踪为以及时间,然后使用这些值UIGestureRecognizerStateEnded
来计算实际的最终速度。它工作得很好。
Here is my code for calculating the velocity. I happen to not be using velocity to figure out the speed of the animation, but rather I'm using it to determine whether the user either panned far enough or flicked quickly enough for the view to move more than half way across the screen and thus triggering the animation between views, but the concept of calculating the final velocity seems applicable to this question. Here's the code:
这是我计算速度的代码。我碰巧没有使用速度来计算动画的速度,而是使用它来确定用户是否平移足够远或快速轻弹以使视图在屏幕上移动超过一半,从而触发视图之间的动画,但计算最终速度的概念似乎适用于这个问题。这是代码:
- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
static CGPoint lastTranslate; // the last value
static CGPoint prevTranslate; // the value before that one
static NSTimeInterval lastTime;
static NSTimeInterval prevTime;
CGPoint translate = [gesture translationInView:self.view];
if (gesture.state == UIGestureRecognizerStateBegan)
{
lastTime = [NSDate timeIntervalSinceReferenceDate];
lastTranslate = translate;
prevTime = lastTime;
prevTranslate = lastTranslate;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
prevTime = lastTime;
prevTranslate = lastTranslate;
lastTime = [NSDate timeIntervalSinceReferenceDate];
lastTranslate = translate;
[self moveSubviewsBy:translate];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
CGPoint swipeVelocity = CGPointZero;
NSTimeInterval seconds = [NSDate timeIntervalSinceReferenceDate] - prevTime;
if (seconds)
{
swipeVelocity = CGPointMake((translate.x - prevTranslate.x) / seconds, (translate.y - prevTranslate.y) / seconds);
}
float inertiaSeconds = 1.0; // let's calculate where that flick would take us this far in the future
CGPoint final = CGPointMake(translate.x + swipeVelocity.x * inertiaSeconds, translate.y + swipeVelocity.y * inertiaSeconds);
[self animateSubviewsUsing:final];
}
}
回答by nikans
In most cases setting UIViewAnimationOptionBeginFromCurrentState
option for UIView
animator is enough to make a flawlessly continued animation.
在大多数情况下UIViewAnimationOptionBeginFromCurrentState
,UIView
动画师的设置选项足以制作完美的连续动画。