在关键帧动画的另一个测试中,我正在组合沿着贝塞尔曲线路径移动UIImageView(称为theImage
)并在移动时缩放它,导致路径末端的图像大2倍。我这样做的初始代码中包含了这些元素来启动动画:
UIImageView* theImage = ....
float scaleFactor = 2.0;
....
theImage.center = destination;
theImage.transform = CGAffineTransformMakeScale(1.0,1.0);
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(theImage.image.size.height*scaleFactor, theImage.image.size.width*scaleFactor)]];
resizeAnimation.fillMode = kCAFillModeBackwards;
resizeAnimation.removedOnCompletion = NO;
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.path = [jdPath path].CGPath;
pathAnimation.fillMode = kCAFillModeBackwards;
pathAnimation.removedOnCompletion = NO;
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:pathAnimation, resizeAnimation, nil];
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.removedOnCompletion = NO;
group.duration = duration;
group.delegate = self;
[theImage.layer addAnimation:group forKey:@"animateImage"];
然后,当动画完成时,我想保留更大尺寸的图像,所以我实现了:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
theImage.transform = CGAffineTransformMakeScale(scaleFactor,scaleFactor);
}
这一切都有效..有点儿。问题是在动画结束时theImage
闪烁了一会儿 - 足以使它看起来很糟糕。我猜这是动画结束时的过渡,我将变换设置为新大小。
在试验这个时,我尝试了一种略微不同的上述形式,但仍然有相同的闪烁:
CAKeyframeAnimation *resizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue* startSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)];
NSValue* endSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, scaleFactor, scaleFactor, 1.0)];
NSArray* sizeKeys = [NSArray arrayWithObjects:startSizeKey, endSizeKey, nil];
[resizeAnimation setValues:sizeKeys];
....
theImage.transform = CGAffineTransformMakeScale(scaleFactor,scaleFactor);
但是当我以与原版相同的大小结束动画时,没有闪烁:
CAKeyframeAnimation *resizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue* startSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)];
NSValue* middleSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, scaleFactor, scaleFactor, 1.0)];
NSValue* endSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)];
NSArray* sizeKeys = [NSArray arrayWithObjects:startSizeKey, middleSizeKey, endSizeKey, nil];
[resizeAnimation setValues:sizeKeys];
....
theImage.transform = CGAffineTransformMakeScale(1.0,1.0);
所以我的大问题是如何在没有闪烁的情况下为此图像设置动画,并在动画结束时以不同的尺寸结束?
编辑3月2日
我的初步测试是将图像缩放。我只是尝试缩小它(IE scaleFactor = 0.4)并且闪烁更加明显,而且我所看到的更加明显。这是一系列事件:
所以似乎步骤4就是我所看到的闪烁。
编辑3月22日
我刚刚向GitHub上传了一个演示项目,该项目展示了沿着贝塞尔路径移动物体的过程。代码可以在PathMove
找到 的博客中写过这篇文章答案 0 :(得分:7)
使用Core Animation为视图的图层设置动画可能很棘手。有几件事令人困惑:
在图层上设置动画不会更改图层的属性。相反,它会更改“表示层”的属性,只要应用动画,它就会替换屏幕上的原始“模型层”。
更改图层的属性通常会向图层添加隐式动画,并将属性名称作为动画的键。因此,如果要显式动画属性,通常需要将属性设置为其最终值,然后添加其键为属性名称的动画,以覆盖隐式动画。
视图通常会在其图层上禁用隐式动画。它还以其他有些神秘的方式在其图层的属性上进行修复。
另外,让您为视图的界限设置动画以进行缩放比较令人困惑,但最后切换到比例变换。
我认为最简单的方法就是尽可能地使用UIView
动画方法,并且只为关键帧动画引入Core Animation。在让UIView
添加自己的动画后,您可以将关键帧动画添加到视图的图层,并且您的关键帧动画将覆盖由UIView
添加的动画。
这对我有用:
- (IBAction)animate:(id)sender {
UIImageView* theImage = self.imageView;
CGFloat scaleFactor = 2;
NSTimeInterval duration = 1;
UIBezierPath *path = [self animationPathFromStartingPoint:theImage.center];
CGPoint destination = [path currentPoint];
[UIView animateWithDuration:duration animations:^{
// UIView will add animations for both of these changes.
theImage.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
theImage.center = destination;
// Prepare my own keypath animation for the layer position.
// The layer position is the same as the view center.
CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
positionAnimation.path = path.CGPath;
// Copy properties from UIView's animation.
CAAnimation *autoAnimation = [theImage.layer animationForKey:@"position"];
positionAnimation.duration = autoAnimation.duration;
positionAnimation.fillMode = autoAnimation.fillMode;
// Replace UIView's animation with my animation.
[theImage.layer addAnimation:positionAnimation forKey:positionAnimation.keyPath];
}];
}
答案 1 :(得分:4)
本周我正在尝试一些CAAnimations,并注意到我的动画结束时出现了闪烁。特别是,我会从圆圈到正方形进行动画制作,同时也可以更改fillColor。
每个CAAnimation都有一个名为removedOnCompletion
的属性,默认为YES。这意味着当动画完成时动画将消失(即过渡,缩放,旋转等),并且您将留下原始图层。
由于您已将removedOnCompletion属性设置为NO,我建议您尝试将动画的执行转移到使用CATransactions,而不是委托和animationDidStop ......
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction setCompletionBlock: ^{ theImage.transform = ...}];
// ... CAAnimation Stuff ... //
[CATransaction commit];
在创建动画之前,您将完成事务的完成块调用,具体如下: http://zearfoss.wordpress.com/2011/02/24/core-animation-catransaction-protip/
以下是我的一种方法:
[CATransaction begin];
CABasicAnimation *animation = ...;
animation.fromValue = ...;
animation.toValue = ...;
[CATransaction setCompletionBlock:^ { self.shadowRadius = _shadowRadius; }];
[self addAnimation:animation forKey:@"animateShadowOpacity"];
[CATransaction commit];
并且,我构建了这个动画,它对我来说很好,最后没有毛刺: 设置和触发器是我在窗口中的自定义方法,我在mousedown上触发动画。
UIImageView *imgView;
UIBezierPath *animationPath;
-(void)setup {
canvas = (C4View *)self.view;
imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"img256.png"]];
imgView.frame = CGRectMake(0, 0, 128, 128);
imgView.center = CGPointMake(384, 128);
[canvas addSubview:imgView];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIImageView animateWithDuration:2.0f animations:^{
[CATransaction begin];
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.duration = 2.0f;
pathAnimation.calculationMode = kCAAnimationPaced;
animationPath = [UIBezierPath bezierPath];
[animationPath moveToPoint:imgView.center];
[animationPath addLineToPoint:CGPointMake(128, 512)];
[animationPath addLineToPoint:CGPointMake(384, 896)];
pathAnimation.path = animationPath.CGPath;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
[imgView.layer addAnimation:pathAnimation forKey:@"animatePosition"];
[CATransaction commit];
CGFloat scaleFactor = 2.0f;
CGRect newFrame = imgView.frame;
newFrame.size.width *= scaleFactor;
newFrame.size.height *= scaleFactor;
newFrame.origin = CGPointMake(256, 0);
imgView.frame = newFrame;
imgView.transform = CGAffineTransformRotate(imgView.transform,90.0*M_PI/180);
}];
}
答案 2 :(得分:3)
CAAnimation
将在末尾闪烁。请记住CAAnimation
是对象属性的临时调整,以便可视化过渡。完成动画后,如果图层的状态仍然是原始的开始状态,那么在您设置最终图层状态之前,这将是暂时显示的,这是您在animationDidStop:
方法中所做的。
此外,您的动画正在调整图层的bounds.size
属性,因此您应该类似地设置最终状态,而不是将变换调整用作最终状态。您还可以使用transform
属性作为动画中的动画属性,而不是bounds.size
。
要解决此问题,请在分配动画后立即将图层的渗透状态更改为所需的终端状态,以便在动画完成时不会出现闪烁,但这样做是为了不会触发隐式动画。动画开始了。具体来说,在您的情况下,您应该在动画设置结束时执行此操作:
UIImageView* theImage = ....
float scaleFactor = 2.0;
....
theImage.center = destination;
theImage.transform = CGAffineTransformMakeScale(1.0,1.0);
CGSize finalSize = CGSizeMake(theImage.image.size.height*scaleFactor, theImage.image.size.width*scaleFactor);
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:finalSize]];
resizeAnimation.fillMode = kCAFillModeBackwards;
resizeAnimation.removedOnCompletion = NO;
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.path = [jdPath path].CGPath;
pathAnimation.fillMode = kCAFillModeBackwards;
pathAnimation.removedOnCompletion = NO;
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:pathAnimation, resizeAnimation, nil];
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.removedOnCompletion = NO;
group.duration = duration;
group.delegate = self;
[theImage.layer addAnimation:group forKey:@"animateImage"];
[CATransaction begin];
[CATransaction setDisableActions:YES];
theImage.bounds = CGRectMake( theImage.bounds.origin.x, theImage.bounds.origin.y, finalSize.width, finalSize.height );
[CATransaction commit];
然后删除animationDidStop:
方法中的转换调整。