我有一个项目被删除的用户界面,我想模仿iOS邮件中的“移动到文件夹”效果。将小写字母图标“抛出”到文件夹中的效果。我的将被倾倒在垃圾箱中。
我尝试在图层上使用CAAnimation
来实现它。至于我在文档中可以看到,我应该能够设置byValue
和toValue
,CAAnimation应该插值。我希望做一个小曲线,所以项目会经过项目开始位置上方和左侧的一点。
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position"];
[animation setDuration:2.0f];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut]];
[animation setFromValue:[NSValue valueWithCGPoint:fromPoint]];
[animation setByValue:[NSValue valueWithCGPoint:byPoint]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(512.0f, 800.0f)]];
[animation setRepeatCount:1.0];
我玩了一段时间,但在我看来,Apple意味着线性插值。 添加byValue不会计算出漂亮的弧或曲线,也不会通过它为项目设置动画。
我该怎么做这样的动画?
感谢您给予的任何帮助。
答案 0 :(得分:30)
(如果你使用的是iOS 6或之前的版本,请不要忘记链接然后import QuartzCore
你可以使用一个跟随路径的动画,方便的是,CAKeyframeAnimation
支持CGPath
,可以从UIBezierPath
获得。斯威夫特3
func animate(view : UIView, fromPoint start : CGPoint, toPoint end: CGPoint)
{
// The animation
let animation = CAKeyframeAnimation(keyPath: "position")
// Animation's path
let path = UIBezierPath()
// Move the "cursor" to the start
path.move(to: start)
// Calculate the control points
let c1 = CGPoint(x: start.x + 64, y: start.y)
let c2 = CGPoint(x: end.x, y: end.y - 128)
// Draw a curve towards the end, using control points
path.addCurve(to: end, controlPoint1: c1, controlPoint2: c2)
// Use this path as the animation's path (casted to CGPath)
animation.path = path.cgPath;
// The other animations properties
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.duration = 1.0
animation.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseIn)
// Apply it
view.layer.add(animation, forKey:"trash")
}
Bezier路径(或Bezier Curves,准确)的工作方式与您在photoshop,烟花,草图中找到的路径完全相同......它们有两个“控制点”,每个顶点一个。例如,我刚刚制作的动画:
像这样工作bezier路径。参见documentation on the specifics,但它基本上是两个点,将弧线拉向某个方向。
关于UIBezierPath
的一个很酷的功能是,您可以使用CAShapeLayer
在屏幕上绘制它们,从而帮助您可视化它将遵循的路径。
// Drawing the path
let *layer = CAShapeLayer()
layer.path = path.cgPath
layer.strokeColor = UIColor.black.cgColor
layer.lineWidth = 1.0
layer.fillColor = nil
self.view.layer.addSublayer(layer)
计算你自己的bezier路径的想法是,你可以使动态完全动态,因此,动画可以根据多种因素改变它将要做的曲线,而不仅仅是硬编码,就像我在例如,控制点可以按如下方式计算:
// Calculate the control points
let factor : CGFloat = 0.5
let deltaX : CGFloat = end.x - start.x
let deltaY : CGFloat = end.y - start.y
let c1 = CGPoint(x: start.x + deltaX * factor, y: start.y)
let c2 = CGPoint(x: end.x , y: end.y - deltaY * factor)
最后一段代码使得这些点与上图相似,但是在可变量中,相对于点形成的三角形,乘以一个等于“张力”值的因子
答案 1 :(得分:7)
使用CABasicAnimation
设置位置动画会使其沿直线移动是完全正确的。还有另一个名为CAKeyframeAnimation
的类用于执行更高级的动画。
values
除了基本动画的toValue
,fromValue
和byValue
,您可以使用values
数组或完整path
来确定值方式。如果您想先将位置设置为侧面然后向下设置动画,则可以传递3个位置的数组(开始,中间,结束)。
CGPoint startPoint = myView.layer.position;
CGPoint endPoint = CGPointMake(512.0f, 800.0f); // or any point
CGPoint midPoint = CGPointMake(endPoint.x, startPoint.y);
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:@"position"];
move.values = @[[NSValue valueWithCGPoint:startPoint],
[NSValue valueWithCGPoint:midPoint],
[NSValue valueWithCGPoint:endPoint]];
move.duration = 2.0f;
myView.layer.position = endPoint; // instead of removeOnCompletion
[myView.layer addAnimation:move forKey:@"move the view"];
如果这样做,您会注意到视图从直线的起点移动到中点,而在另一条直线上移动到终点。缺少使其从中间点开始到结束的部分是更改动画的calculationMode
。
move.calculationMode = kCAAnimationCubic;
您可以通过更改tensionValues
,continuityValues
和biasValues
属性来控制它。如果想要更精细的控制,可以定义自己的路径而不是values
数组。
您可以创建任何路径并指定该属性应遵循该路径。我在这里使用一个简单的弧
path
答案 2 :(得分:3)
试试这个肯定会解决你的问题,我在我的项目中使用了这个:
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10, 126, 320, 24)] autorelease];
label.text = @"Animate image into trash button";
label.textAlignment = UITextAlignmentCenter;
[label sizeToFit];
[scrollView addSubview:label];
UIImageView *icon = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"carmodel.png"]] autorelease];
icon.center = CGPointMake(290, 150);
icon.tag = ButtonActionsBehaviorTypeAnimateTrash;
[scrollView addSubview:icon];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.center = CGPointMake(40, 200);
button.tag = ButtonActionsBehaviorTypeAnimateTrash;
[button setTitle:@"Delete Icon" forState:UIControlStateNormal];
[button sizeToFit];
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:button];
[scrollView bringSubviewToFront:icon];
- (void)buttonClicked:(id)sender {
UIView *senderView = (UIView*)sender;
if (![senderView isKindOfClass:[UIView class]])
return;
switch (senderView.tag) {
case ButtonActionsBehaviorTypeExpand: {
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.125;
anim.repeatCount = 1;
anim.autoreverses = YES;
anim.removedOnCompletion = YES;
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)];
[senderView.layer addAnimation:anim forKey:nil];
break;
}
case ButtonActionsBehaviorTypeAnimateTrash: {
UIView *icon = nil;
for (UIView *theview in senderView.superview.subviews) {
if (theview.tag != ButtonActionsBehaviorTypeAnimateTrash)
continue;
if ([theview isKindOfClass:[UIImageView class]]) {
icon = theview;
break;
}
}
if (!icon)
return;
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:icon.center];
[movePath addQuadCurveToPoint:senderView.center
controlPoint:CGPointMake(senderView.center.x, icon.center.y)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = YES;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
opacityAnim.removedOnCompletion = YES;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
animGroup.duration = 0.5;
[icon.layer addAnimation:animGroup forKey:nil];
break;
}
}
}
答案 3 :(得分:1)
我发现了怎么做。实际上可以分别为X和Y设置动画。如果你在同一时间(2.0秒以下)为它们设置动画并设置不同的计时功能,它会使它看起来像是从弧线移动而不是从开始到结束值的直线。要调整弧度,您需要设置不同的计时功能。不确定CAAnimation是否支持任何“性感”计时功能。
const CFTimeInterval DURATION = 2.0f;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
[animation setDuration:DURATION];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[animation setFromValue:[NSNumber numberWithDouble:400.0]];
[animation setToValue:[NSNumber numberWithDouble:0.0]];
[animation setRepeatCount:1.0];
[animation setDelegate:self];
[myview.layer addAnimation:animation forKey:@"animatePositionY"];
animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
[animation setDuration:DURATION];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[animation setFromValue:[NSNumber numberWithDouble:300.0]];
[animation setToValue:[NSNumber numberWithDouble:0.0]];
[animation setRepeatCount:1.0];
[animation setDelegate:self];
[myview.layer addAnimation:animation forKey:@"animatePositionX"];
编辑:
应该可以使用https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/CAMediaTimingFunction_class/Introduction/Introduction.html(由functionWithControlPoints ::::引入的CAMediaTimingFunction)来改变计时功能 这是一个“立方贝塞尔曲线”。我确信谷歌有答案。 http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves: - )
答案 4 :(得分:0)