Iphone &引用;推;Objective-C中的动画
我有两个视图:A和B。A位于屏幕顶部,B位于屏幕底部 当用户按下按钮时,视图B将使用EaseInEaseOut bezier曲线向上设置动画,直到其达到y=0。当B在它到达目的地的途中,当它碰到A时,它应该向上推A。换句话说,当B在从底部到顶部的过渡过程中通过某个y坐标(A的y原点+高度)时,A应该粘在B上,所以B似乎向上推A 到目前为止,我所尝试的:Iphone &引用;推;Objective-C中的动画,iphone,ios,objective-c,core-animation,Iphone,Ios,Objective C,Core Animation,我有两个视图:A和B。A位于屏幕顶部,B位于屏幕底部 当用户按下按钮时,视图B将使用EaseInEaseOut bezier曲线向上设置动画,直到其达到y=0。当B在它到达目的地的途中,当它碰到A时,它应该向上推A。换句话说,当B在从底部到顶部的过渡过程中通过某个y坐标(A的y原点+高度)时,A应该粘在B上,所以B似乎向上推A 到目前为止,我所尝试的: 用户按下按钮后,立即将target+选择器注册到CADisplayLink。在该选择器中,通过访问视图B的presentationLayer请
- 用户按下按钮后,立即将target+选择器注册到CADisplayLink。在该选择器中,通过访问视图B的presentationLayer请求视图B的y坐标,并相应地调整A的y坐标。但是,这种方法不够精确:presentationLayer的帧在屏幕上B的当前位置后面(这可能是因为-presentationLayer在当前时间重新计算动画视图的位置,这需要超过1帧)。当我增加B的动画持续时间时,此方法效果良好
- 用户按下按钮后,立即将target+选择器注册到CADisplayLink。在该选择器内,通过求解bezier方程,计算B的当前y坐标,该方程的x=经过的时间/动画持续时间(应返回所行驶的距离/总距离的商)。为此,我使用了苹果的开源UnitBezier.h()。然而,结果并不正确
animationWithDuration
:您可以将动画分解为两个嵌套动画,使用“ease-in”设置“B”向“A”移动的动画,然后使用“ease-out”设置“B”和“A”向上移动的动画。这里唯一的技巧是确保这两个持续时间
值有意义,以便动画的速度看起来不会改变
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的当前帧
,并相应地调整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;
}
}
-(无效)开始显示链接
{
self.displayLink=[CADisplayLink displayLinkWithTarget:self-selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]forMode:NSDefaultRunLoopMode];
}
-(无效)停止显示链接
{
[self.displayLink失效];
self.displayLink=nil;
}
-(无效)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=自我分析框架;
frame.origin.y=presentationLayer.frame.origin.y-self.a.frame.size.height;
self.a.frame=框架;
}
}
您说您尝试了“设置B动画并使用显示链接更新A”技术,结果“A”落后于“B”。从理论上讲,您可以设置新视图“C”的动画,然后在显示链接中相应地调整B和a的帧,这将消除任何延迟(相对于彼此)。您可以尝试以下算法: 1) 将A和B放入UIView(即UIView*gropedView) 2) 改变B.y,直到它等于A.y+A.height(因此B将位于A的正下方)
3) 动画组视图在类似的情况下,我采用了一种方法,即两个视图都在整个距离内设置动画,但使用一个带有时间偏移和a动画开始时间的CAAnimationGroup,以防止在B点击它之前实际显示它。(请参阅)您的解决方案听起来更简单。绝妙而简单的解决方案。谢谢