【问题标题】:"Push" animation in Objective-CObjective-C 中的“推”动画
【发布时间】:2013-03-12 16:16:29
【问题描述】:
我有两个视图:A 和 B。A 位于屏幕顶部,B 位于屏幕底部。
当用户按下按钮时,视图 B 会以 EaseInEaseOut 贝塞尔曲线向上动画,直到到达 y = 0。当 B 正在前往其目的地的途中,它应该在碰到 A 时将 A 向上推。换句话说,当 B 在从底部到顶部的过渡过程中通过了某个 y 坐标(A 的 y 原点 + 高度)时,A 应该粘在 B 上,所以看起来 B 将 A 向上推。
到目前为止我所尝试的:
- 在用户按下按钮后立即将目标 + 选择器注册到 CADisplayLink。在这个选择器中,通过访问它的presentationLayer 请求视图B 的y 坐标,并相应地调整A 的y 坐标。但是,这种方法结果不够准确:presentationLayer 的帧落后于 B 在屏幕上的当前位置(这可能是因为 -presentationLayer 重新计算动画视图在当前时间的位置,这需要超过 1 帧) .当我增加 B 的动画持续时间时,这种方法效果很好。
- 在用户按下按钮后立即将目标 + 选择器注册到 CADisplayLink。在此选择器中,通过求解贝塞尔方程 x = 经过时间/动画持续时间(应返回行进距离商/总距离)来计算 B 的当前 y 坐标。为此,我使用了 Apple 的开源 UnitBezier.h (http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)。但是,结果不正确。
对我接下来可以尝试什么有什么建议吗?
【问题讨论】:
标签:
iphone
ios
objective-c
core-animation
【解决方案1】:
两个简单的解决方案:
-
仅使用animationWithDuration:您可以将动画分成两个嵌套动画,使用“ease in”为“B”移动到“A”设置动画,然后使用“ease out”为剩下的就是“B”和“A”。这里唯一的技巧是确保两个 duration 值有意义,这样动画的速度不会出现变化。
CGFloat animationDuration = 0.5;
CGFloat firstPortionDistance = self.b.frame.origin.y - (self.a.frame.origin.y + self.a.frame.size.height);
CGFloat secondPortionDistance = self.a.frame.size.height;
CGFloat firstPortionDuration = animationDuration * firstPortionDistance / (firstPortionDistance + secondPortionDistance);
CGFloat secondPortionDuration = animationDuration * secondPortionDistance / (firstPortionDistance + secondPortionDistance);
[UIView animateWithDuration:firstPortionDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
CGRect frame = self.b.frame;
frame.origin.y -= firstPortionDistance;
self.b.frame = frame;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:secondPortionDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect frame = self.b.frame;
frame.origin.y -= secondPortionDistance;
self.b.frame = frame;
frame = self.a.frame;
frame.origin.y -= secondPortionDistance;
self.a.frame = frame;
}
completion:nil];
}];
-
您可以让animateWithDuration 处理“B”的完整动画,然后使用CADisplayLink 并使用presentationLayer 检索B 的当前frame 并相应地调整A 的帧:
[self startDisplayLink];
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
CGRect frame = self.b.frame;
frame.origin.y = self.a.frame.origin.y;
self.b.frame = frame;
}
completion:^(BOOL finished) {
[self stopDisplayLink];
}];
其中启动、停止和处理显示链接的方法定义如下:
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
CALayer *presentationLayer = self.b.layer.presentationLayer;
if (presentationLayer.frame.origin.y < (self.a.frame.origin.y + self.a.frame.size.height))
{
CGRect frame = self.a.frame;
frame.origin.y = presentationLayer.frame.origin.y - self.a.frame.size.height;
self.a.frame = frame;
}
}
【解决方案2】:
您说您尝试了“动画 B 并使用显示链接更新 A”技术,结果导致“A”落后于“B”。理论上,您可以为新视图“C”设置动画,然后在显示链接中相应地调整 B 和 A 的帧,这应该消除任何滞后(相对于彼此)。
【解决方案3】:
您可以尝试以下算法:
1)将A和B放在UIView中(即UIview *gropedView)
2)改变 B.y 直到它等于 A.y+A.height(所以 B 将在 A 的正下方)
3)动画分组视图