I have a mask that I've build thanks to a layer and an image. I want to move this layer with little "jumps" (without a nice translation animation).
The problem is that there is an animation happening whenever I move the frame of the mask layer. I've tried calling [maskLayer removeAllAnimations]; but it doesn't change anything.
In my code, I don't put any animation at all, so I don't understand where this animation comes from.
When I launch this function :
-(void) moveAlternativeMask:(CALayer*) maskLayer {
NSLog (#"moveAlternativeMask %#", NSStringFromCGRect(maskLayer.frame));
// Change the frame of the layer
CGRect originalFrame = maskLayer.frame;
CGFloat newYPos = maskLayer.frame.origin.y + 5.;
maskLayer.frame = CGRectMake (originalFrame.origin.x, newYPos, originalFrame.size.width, originalFrame.size.height);
// Try removing all animations on the layer
NSLog (#"Animations");
for (NSString* animationKey in maskLayer.animationKeys) {
NSLog(#" Animation With Key %#", animationKey);
}
NSLog (#"End animations");
[maskLayer removeAllAnimations];
NSLog (#"Animations after removing");
for (NSString* animationKey in maskLayer.animationKeys) {
NSLog(#" Animation With Key %#", animationKey);
}
NSLog (#"End animations after removing");
// Launch the animation with a small delay
[self performSelector:#selector(moveAlternativeMask:) withObject:maskLayer afterDelay:0.5];
}
I get this log :
----moveAlternativeMask {{0, 200}, {768, 643}} ----
Animations
Animation with key : position
End animations
Animations after removing
End animations after removing
---moveAlternativeMask {{0, 205}, {768, 643}}
Animations
Animation with key : position
End animations
Animations after removing
End animations after removing
---moveAlternativeMask {{0, 210}, {768, 643}}
Animations
Animation with key : position
End animations
Animations after removing
End animations after removing
So the layer is moved as I want, the only problem is that the "position" animation keep coming back...
Note that the CALayer was build like this :
UIImage *maskImage = [UIImage imageNamed:#"masque2.png"];
self.maskLayer = [CALayer layer];
self.maskLayer.contents = (id) maskImage.CGImage;
self.maskLayer.frame = CGRectMake(0, ORIGINAL_Y_POSITION, 768, 643);
[myView.layer setMask:self.maskLayer];
Hmm, very strange. I have 2 ideas:
Is it feasible to try doing this with a UIView instead?
Could you perhaps try removeFromSuperview and addSubview instead of just changing the frame?
Related
My app has to do a calculation and animation based on a choice the user makes and I need to update a UILabel before it is used in the animation, the problem is whenever I set the UILabel text it messes up the animation and I have no idea why. I have tried [self.view.layer removeAllAnimations]; before the changing the UILabel text and still no good. I even tried programmatically calling an IBAction and changing UILabel text from that block and it still messes up the animation. Nothing seems to work and it is definitely about timing as changing the UILabel text in viewDidLoad does work fine. I also checked if it is a Storyboard problem by changing another UILabel's text before the animation and the same problem occurs. If it helps at all here is the animation code:
-(IBAction) swipeDown:(UISwipeGestureRecognizer *)recognizer{
multiply.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
subtract.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
add.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
divide.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
circle.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
signcircle.frame = CGRectMake(145.0f, 70.0f, signcircle.frame.size.width,signcircle.frame.size.height);
sign.frame = CGRectMake(152.0f, 68.0f, sign.frame.size.width,sign.frame.size.height);
type=#"multiply";
if(fivebutton.highlighted){
circle.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
multiply.transform = CGAffineTransformMakeScale(1.35f, 1.35f);
multiply.frame = CGRectMake(120.0f, 248.0f, multiply.frame.size.width, multiply.frame.size.height);
[UIView animateWithDuration:.25f animations:^{
add.frame = CGRectMake(130.0f, 202.0f, add.frame.size.width, add.frame.size.height);
add.alpha=1.0;
subtract.frame = CGRectMake(82.0f, 255.0f, subtract.frame.size.width, subtract.frame.size.height);
subtract.alpha=1.0;
divide.frame = CGRectMake(178.0f, 255.0f, divide.frame.size.width, divide.frame.size.height);
divide.alpha=1.0;
multiply.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
multiply.frame = CGRectMake(130.0f, 305.0f, multiply.frame.size.width, divide.frame.size.height);
multiply.alpha=1.0;
circle.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
circle.alpha=1.0;
UIImage *downimage = [UIImage imageNamed: #"multiplydown.png"];
[multiply setImage:downimage];
onebutton.alpha=0.1;
one.alpha=0.1;
twobutton.alpha=0.1;
two.alpha=0.1;
threebutton.alpha=0.1;
three.alpha=0.1;
fourbutton.alpha=0.1;
four.alpha=0.1;
sixbutton.alpha=0.1;
six.alpha=0.1;
fivebutton.alpha=0.0;
sevenbutton.alpha=0.1;
seven.alpha=0.1;
eightbutton.alpha=0.1;
eight.alpha=0.1;
ninebutton.alpha=0.1;
nine.alpha=0.1;
zerobutton.alpha=0.1;
zero.alpha=0.1;
decimalbutton.alpha=0.1;
decimal.alpha=0.1;
equalsbutton.alpha=0.1;
equals.alpha=0.1;
}];
[UIView commitAnimations];
}
In this case the UILabel text change causes "add","subtract","divide" and "multiply" (UIImageView's) to merge together instead of spreading as the animation is supposed to.
-(void) fiveTapEnded{
var2.hidden=NO;
if ([type isEqual:#"add"]) {
NSLog(#"Is add");
}
if ([type isEqual:#"subtract"]) {
NSLog(#"Is subtract");
}
if ([type isEqual:#"multiply"]) {
var1.textAlignment = NSTextAlignmentRight;
var1.frame = CGRectMake(40.0f, 70.0f, var1.frame.size.width,var1.frame.size.height);
var2.frame = CGRectMake(145.0f, 70.0f, var2.frame.size.width,var2.frame.size.height);
[UIView animateWithDuration:.25f animations:^{
add.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
add.alpha=0.0;
multiply.transform = CGAffineTransformMakeScale(1.5f, 1.5f);
multiply.alpha=0.0;
divide.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
divide.alpha=0.0;
circle.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
circle.alpha=0.0;
subtract.alpha=0.0;
var1.frame = CGRectMake(0.0f, 20.0f, var1.frame.size.width,var1.frame.size.height);
var2.frame = CGRectMake(185.0f, 20.0f, var2.frame.size.width,var2.frame.size.height);
signcircle.frame = CGRectMake(145.0f, 30.0f, signcircle.frame.size.width,signcircle.frame.size.height);
sign.frame = CGRectMake(152.0f, 26.5f, sign.frame.size.width,sign.frame.size.height);
result.frame = CGRectMake(8.0f, 100.0f, result.frame.size.width,result.frame.size.height);
signcircle.alpha=1.0;
sign.alpha=1.0;
var2.alpha=1.0;
result.alpha=1.0;
one.alpha=1.0;
onebutton.alpha=1.0;
twobutton.alpha=1.0;
two.alpha=1.0;
three.alpha=1.0;
threebutton.alpha=1.0;
fourbutton.alpha=1.0;
four.alpha=1.0;
sixbutton.alpha=1.0;
six.alpha=1.0;
five.alpha=1.0;
fivebutton.alpha=1.0;;
sevenbutton.alpha=1.0;
seven.alpha=1.0;
eightbutton.alpha=1.0;
eight.alpha=1.0;
ninebutton.alpha=1.0;
nine.alpha=1.0;
zerobutton.alpha=1.0;
zero.alpha=1.0;
decimalbutton.alpha=1.0;
decimal.alpha=1.0;
equalsbutton.alpha=1.0;
equals.alpha=1.0;
}];
[self.view.layer removeAllAnimations];
[self functionToBeCalled:self];
}
if ([type isEqual:#"divide"]) {
NSLog(#"Is divide");
}
}
In this case it causes "var1" and "var2" (UILabel's) to not move upwards.
I understand its confusing, I've been searching everywhere and can't find a similar problem so this was kind of my last resort, if anyone has any solution or tips that would be greatly appreciated.
If, by "messes up animation", you mean that it's going back to its starting location, then you probably have autolayout on. The autolayout constraints are automatically reapplied when you change the UILabel, and thus any animations that entail the changing of the frame will be thwarted. Bottom line, when using autolayout with constraints that dictate the layout, you should not be changing frame or center values manually.
That obviously leaves two possible remedies:
Simplest, you could turn off auto layout. Then the setting of the label won't be triggering any constraints to be applied. See NSTimer blocks other animations for very similar issue.
If you want to keep auto layout turned on, then you shouldn't be animating by changing the frame (or center) properties of controls, but rather you should create IBOutlet references for the appropriate constraints, and then animate them by changing the constant property. See Animating an image view to slide upwards
Here is the code which draws a vertical line in graph.
-(void)drawRect:(CGRect)rect
{
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
UIBezierPath *breakFastValuePath = [UIBezierPath bezierPath];
[breakFastValuePath moveToPoint:CGPointMake(89, 288)];
[breakFastValuePath addLineToPoint:CGPointMake(89,288-delegate.breakFastTotalamt)];
[breakFastValuePath closePath];
[[UIColor greenColor] setStroke];
breakFastValuePath.lineWidth = 10;
[breakFastValuePath stroke];
}
How to make the line animating from starting point to end point wen view is loaded?
You can animate the strokeEnd from 0.0 to 1.0 to give the effect that the line along its path from start to end. Look at this question (about drawing a circle in Core Animation) for reference.
I believe you need to take a different approach.
I will show you an example for a horizontal line, the vertical case will be very similar.
Use a normal UIView to represent your line with an initial frame like:
UIView *lineView = [[UIView alloc] initWithFrame:
CGRectMake(startX,startY,1,lineThickness)];//Line starts as 1 pixel long.
//Then you need to animate this inside loadView:
[UIView animateWithDuration:1//Amount of time the animation takes.
delay:0//Amount of time after which animation starts.
options: UIViewAnimationCurveEaseOut//How the animation will behave.
animations:^{
//here you can either set a CGAffineTransform, or change your view's frame.
//Both will work just fine.
lineView = CGAffineTransformMakeScale (
scaleForX,//say 100, Now the line will be a 100 pixels long.
scaleForY//say 1, Maintain line thickness.
//direction.
//Note* you could also set the frame for a similar effect.
//view's frame.
//lineView.frame = CGRectMake(startX,startY,finalLength,lineThickness)
}
completion:^(BOOL finished){//This block is called when the animation completes.
NSLog(#"Done!");
}];
I have a UIView object that rotates using CALayer's transform:
// Create uiview object.
UIImageView *block = [[UIImageView alloc] initWithFrame....]
// Apply rotation.
CATransform3D basicTrans = CATransform3DIdentity;
basicTrans.m34 = 1.0/-distance;
blockImage.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);
After rotating the edges of the object are not antialiasing. I need to antialias them.
Help me, please. How can it be done?
One way to do this is by placing the image inside another view that's 5 pixels bigger. The bigger view should have a transparent rasterized border that will smooth the edges of the UIImageView:
view.layer.borderWidth = 3;
view.layer.borderColor = [UIColor clearColor].CGColor;
view.layer.shouldRasterize = YES;
view.layer.rasterizationScale = [[UIScreen mainScreen] scale];
Then, place your UIImageView inside this parent view and center it (With 2.5 pixels around each edge).
Finally, rotate the parent view instead of the image view.
It works very well - you can also encapsulate the whole thing in class that creates the hierarchy.
Simply add this key-value pair to your Info.plist: UIViewEdgeAntialiasing set to YES.
check allowsEdgeAntialiasing property of CALayer.
block.layer.allowsEdgeAntialiasing = YES; // iOS7 and above.
I had a similar issue when rotating around the z-axis. Setting shouldRasterize = YES prevented the jagged edges however it came at a performance cost. In my case I was re-using the views (and its layers) and keeping the shouldRasterize = YES was slowing things down.
The solution was, to turn off rasterization right after I didn't need it anymore. However since animation runs on another thread, there was no way of knowing when the animation was complete...until I found out about an extremely useful CATransaction method. This is an actual code that I used and it should illustrate its use:
// Create a key frame animation
CAKeyframeAnimation *wiggle = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
NSInteger frequency = 5; // Higher value for faster vibration
NSInteger amplitude = 25; // Higher value for lower amplitude
// Create the values it will pass through
NSMutableArray *valuesArray = [[NSMutableArray alloc] init];
NSInteger direction = 1;
[valuesArray addObject:#0.0];
for (NSInteger i = frequency; i > 0; i--, direction *= -1) {
[valuesArray addObject:#((direction * M_PI_4 * (CGFloat)i / (CGFloat)amplitude))];
}
[valuesArray addObject:#0.0];
[wiggle setValues:valuesArray];
// Set the duration
[wiggle setAdditive:YES];
[wiggle setValueFunction:[CAValueFunction functionWithName:kCAValueFunctionRotateZ]];
[wiggle setDuration:0.6];
// Turn on rasterization to prevent jagged edges (anti-aliasing issues)
viewToRotate.layer.shouldRasterize = YES;
// ************ Important step **************
// Very usefull method. Block returns after ALL animations have completed.
[CATransaction setCompletionBlock:^{
viewToRotate.layer.shouldRasterize = NO;
}];
// Animate the layer
[viewToRotate.layer addAnimation:wiggle forKey:#"wiggleAnimation"];
worked like a charm for me.
I have not tried using this with implicit animations (i.e. animations that happen due to value change in animatable property for a non-view associated layer), however I would expect it to work as long as the CATransaction method is called before the property change, just as a guarantee the block is given to CATransaction before an animation starts.
I have a UIView subclass which uses a CAShapeLayer mask on its CALayer. The mask uses a distinct shape, with three rounded corners and a cut out rectangle in the remaining corner.
When I resize my UIView using a standard animation block, the UIView itself and its CALayer resize just fine. The mask, however, is applied instantly, which leads to some drawing issues.
I've tried animating the mask's resizing using a CABasicAnimation but didn't have any luck getting the resizing animated.
Can I somehow achieve an animated resizing effect on the mask? Do I need to get rid of the mask, or will I have to change something about the way I currently draw the mask (using - (void)drawInContext:(CGContextRef)ctx).
Cheers,
Alex
I found the solution to this problem. Other answers are partially correct and are helpful.
The following points are important to understanding the solution:
The mask property is not animatable itself.
Since the mask is a CALayer it can be animated on its own.
Frame is not animatable, use bounds and position. This may not apply to you(if you weren't trying to animate the frame), but was an issue for me. (See Apple QA 1620)
A view layer's mask is not tied to UIView so it will not receive the core animation transaction that is applied to the view's layer.
We are modifying the CALayer directly, so we can't expect that UIView will have any idea of what we are trying to do, so the UIView animation won't create the core animation transaction to include changes to our properties.
In order to solve, we are going to have to tap into Core Animation ourselves, and can't rely on the UIView animation block to do the work for us.
Simply create a CATransaction with the same duration that you are using with [UIView animateWithDuration:...]. This will create a separate animation, but if your durations and easing function is the same, it should animate exactly with the other animations in your animation block.
NSTimeInterval duration = 0.5;// match this to the value of the UIView animateWithDuration: call
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration];
self.myView.layer.mask.position = CGPointMake(newX, 0);
self.myView.layer.mask.bounds = CGRectMake(0, 0, newWidth, newHeight);
[CATransaction commit];
I use a CAShapeLayer to mask a UIView by setting self.layer.mask to that shape layer.
To animate the mask whenever the size of the view changes I overwrote the -setBounds: to animate the mask layer path if the bounds are changed during an animation.
Here's how I implemented it:
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
CAPropertyAnimation *boundsAnimation = (CABasicAnimation *)[self.layer animationForKey:#"bounds"];
// update the mask
self.maskLayer.frame = self.layer.bounds;
// if the bounds change happens within an animation, also animate the mask path
if (!boundsAnimation) {
self.maskLayer.path = [self createMaskPath];
} else {
// copying the original animation allows us to keep all animation settings
CABasicAnimation *animation = [boundsAnimation copy];
animation.keyPath = #"path";
CGPathRef newPath = [self createMaskPath];
animation.fromValue = (id)self.maskLayer.path;
animation.toValue = (__bridge id)newPath;
self.maskLayer.path = newPath;
[self.maskLayer addAnimation:animation forKey:#"path"];
}
}
(For the example self.maskLayer is set to `self.layer.mask)
My -createMaskPath calculates the CGPathRef that I use to mask the view. I also update the mask path in -layoutSubviews.
The mask property of CALayer is not animatable which explains your lack of luck in that direction.
Does the drawing of your mask depend on the frame/bounds of the mask? (Can you provide some code?) Does the mask have needsDisplayOnBoundsChange property set?
Cheers,
Corin
To animate the bounds change of the mask layer of a UIView: subclass UIView, and animate the mask with a CATransaction - similar to Kekodas answer but more general:
#implementation UIMaskView
- (void) layoutSubviews {
[super layoutSubviews];
CAAnimation* animation = [self.layer animationForKey:#"bounds"];
if (animation) {
[CATransaction begin];
[CATransaction setAnimationDuration:animation.duration];
}
self.layer.mask.bounds = self.layer.bounds;
if (animation) {
[CATransaction commit];
}
}
#end
The mask parameter doesn't animate, but you can animate the layer which is set as the mask...
If you animate the CAShapeLayer's Path property, that should animate the mask. I can verify that this works from my own projects. Not sure about using a non-vector mask though. Have you tried animating the contents property of the mask?
Thanks,
Jon
I couldn't find any programmatical solution so I just draw an png image with correct shape and alpha values and used that instead. That way I don't need to use a mask...
It is possible to animate the mask change.
I prefer to use CAShapeLayer as the mask layer. It is very convenient to animate a mask change with the help of property path.
Before animate any change, dump the content of the source into an instance CGImageRef, and create a new layer for animation. Hide the original layer during the animation and show it when animation ends.
The following is a sample code for creating key animation on property path.
If you want to create your own path animation, make sure that there are always same number of points in the paths.
- (CALayer*)_mosaicMergeLayer:(CGRect)bounds content:(CGImageRef)content isUp:(BOOL)isUp {
CALayer* layer = [CALayer layer];
layer.frame = bounds;
layer.backgroundColor = [[UIColor clearColor] CGColor];
layer.contents = (id)content;
CAShapeLayer* maskLayer = [CAShapeLayer layer];
maskLayer.fillColor = [[UIColor blackColor] CGColor];
maskLayer.frame = bounds;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.path = ( isUp ? [self _maskArrowUp:-bounds.size.height*2] : [self _maskArrowDown:bounds.size.height*2] );
layer.mask = maskLayer;
CAKeyframeAnimation* ani = [CAKeyframeAnimation animationWithKeyPath:#"path"];
ani.removedOnCompletion = YES;
ani.duration = 0.3f;
ani.fillMode = kCAFillModeForwards;
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
NSArray* values = ( isUp ?
[NSArray arrayWithObjects:
(id)[self _maskArrowUp:0],
(id)[self _maskArrowUp:-ceilf(bounds.size.height*1.2)],
nil]
:
[NSArray arrayWithObjects:
(id)[self _maskArrowDown:0],
(id)[self _maskArrowDown:bounds.size.height],
nil]
);
ani.values = values;
ani.delegate = self;
[maskLayer addAnimation:ani forKey:nil];
return layer;
}
- (void)_startMosaicMergeAni:(BOOL)up {
CALayer* overlayer = self.aniLayer;
CGRect bounds = overlayer.bounds;
self.firstHalfAni = NO;
CALayer* frontLayer = nil;
frontLayer = [self _mosaicMergeLayer:bounds
content:self.toViewSnapshot
isUp:up];
overlayer.contents = (id)self.fromViewSnapshot;
[overlayer addSublayer:frontLayer];
}
Swift 3+ answer based on Kekoa's solution:
let duration = 0.15 //match this to the value of the UIView.animate(withDuration:) call
CATransaction.begin()
CATransaction.setValue(duration, forKey: kCATransactionAnimationDuration)
myView.layer.mask.position = CGPoint(x: [new X], y: [new Y]) //just an example
CATransaction.commit()
Swift implementation of #stigi answer, my mask layer is called shape Layer
override var bounds: CGRect {
didSet {
// debugPrint(self.layer.animationKeys()) //Very useful for know if animation is happening and key name
let propertyAnimation = self.layer.animation(forKey: "bounds.size")
self.shapeLayer?.frame = self.layer.bounds
// if the bounds change happens within an animation, also animate the mask path
if let boundAnimation = propertyAnimation as? CABasicAnimation {
// copying the original animation allows us to keep all animation settings
if let basicAnimation = boundAnimation.copy() as? CABasicAnimation {
basicAnimation.keyPath = "path"
let newPath = UIBezierPathUtils.customShapePath(rect: self.layer.bounds, cornersTriangleSize: cornerTriangleSize).cgPath
basicAnimation.fromValue = self.shapeLayer?.path
basicAnimation.toValue = newPath
self.shapeLayer?.path = newPath
self.shapeLayer?.add(basicAnimation, forKey: "path")
}
} else {
self.shapeLayer?.path = UIBezierPathUtils.customShapePath(rect: self.layer.bounds, cornersTriangleSize: cornerTriangleSize).cgPath
}
}
}
i am trying to implement a graph which a uiview draws. There are 3 UIViews to animate a left/right slide. The problem is, that i can't cancel the UIView animation. So I replaced the UIViews by CALayer. Now, the question is if CALayer is appicable for this? Is it normal to draw on a CALayer like this? And why is a CALayer so slow when I manipulate the frame properties.
CGRect frame = curve.frame;
frame.origin.x -= diffX;
[curve setFrame:frame];
Is there a alternativ?
P.S. I am a german guy. Sorry for mistakes.
I got the animation with CATransaction, but now I will animate a x move with CABasicAnimation. That's no problem expect that the position of the layer go back to the previous x.
CABasicAnimation *theAnimation;
theAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
theAnimation.delegate = self;
theAnimation.duration = 1.0;
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
CGPoint position = [[curves objectAtIndex:i]position];
position.x = [[curves objectAtIndex:i]position].x - diffX;
[theAnimation setToValue:[NSValue valueWithCGPoint:position]];
[[curves objectAtIndex:i] addAnimation:theAnimation forKey:[NSString stringWithFormat: #"translate.x.%d", index]];
The position changes the position (e.g. xStart = 100, xEnd = 200), but when the animation ends the layer goes back to the beginning x (e.g. x = 100).
Is that normal? How can I solve this problem, that the end position doesn't change anymore?
I tried to changed the removeOnComplete property but that didn't effect.
Hope for help.
Markus
Not sure what you mean by 'slow', but setting the frame of a CALayer in this way uses 'implicit animation'. That is, it will animated the transition from the old frame to the new frame. You can turn this off:
[CATransaction begin];
[CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
[curve setFrame:frame];
[CATransaction commit];
However, this is usually considered an advantage of CALayer. You way want to just use UIViews here, which will not, by default, animate transitions such as this.
Instead of setting the destination position in theAnimation, just set the position property of the thing you want to move.
When you addAnimation:theAnimation, you're setting the "visual style" of any changes to the keyPath property you specified.
When you change the position of the object that the animation is attached to, say from (0,0) to (500,500), CoreAnimation will animate the change for you. The theAnimation object doesn't need the start and end positions, since the underlying object has them.