I have an AVCaptureVideoPreviewLayer on screen. I want to change its position instantly after the user touches a button.
I am able to change its position using the following code, but it is not happening instantly but with some kind of smooth liner transition.
CGRect frame = self.preview.frame;
frame.origin.x = previewLeft;
frame.origin.y = previewTop;
self.preview.frame = frame;
How can I make it to change its position without any kind of intrinsic animation?
CALayer.frame is a computed property, but it cannot be explicitly animated. By setting it, you're also setting position, which is implicitly animated.
You can temporarily suppress all actions, the mechanism by which Core Animation creates its default layer animations:
Swift code:
CATransaction.begin()
CATransaction.setDisableActions(true)
self.preview.frame = frame;
CATransaction.commit()
Objective-C code:
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.preview.frame = frame;
[CATransaction commit];
LucasTizma's answer is perfect.
Besides, there is a trick to solve this issue.
Just slow down the window speed:
[(CALayer *)[[[[UIApplication sharedApplication] windows] objectAtIndex:0] layer] setSpeed:0.9];
Don't forget to correct it to 1.0f after your transition.
Related
I'm trying to animate some markers initialised with a picture as follows RMMarker *marker = [[RMMarker alloc] initWithUIImage:lImage anchorPoint:lPoint];
I get a static image on my map with no problem.
Now, I need it to be more visible than other annotations, so I want to make it blink.
Here's what I already tried:
Creating scale animation on annotation layer
[CATransaction begin];
[CATransaction setAnimationDuration:0.70];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
CABasicAnimation *bounceAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
bounceAnimation.repeatCount = MAXFLOAT;
bounceAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1.0)];
bounceAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.8, 0.8, 1.0)];
bounceAnimation.removedOnCompletion = NO;
bounceAnimation.autoreverses = YES;
[layer addAnimation:bounceAnimation forKey:#"animateScale"];
[CATransaction commit];
This is working well, except that my callout view is also blinking as it shares the same layer (and Mapbox messes with the animation during the tracking mode, resulting in an incorrect orientation as it is reseted with every move of the user anyway).
Creating blink animation on annotation layer
[CATransaction begin];
[CATransaction setAnimationDuration:0.60];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.repeatCount = MAXFLOAT;
opacityAnimation.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnimation.toValue = [NSNumber numberWithFloat:0.3];
opacityAnimation.removedOnCompletion = NO;
opacityAnimation.autoreverses = YES;
[layer addAnimation:opacityAnimation forKey:#"animateOpacity"];
[CATransaction commit];
This is less noticeable, but the annotation orientation is correct this time. I still have my callout view blinking on the layer though.
What I want to do
I would need a way to animate the marker image without affecting the layer.
The best animation I'd use would be the first one I tried, without the rotation problem.
So far I couldn't animate the UIImage as it doesn't have the addAnimation: forKey: call.
I've never played much with animations, so any help or guidance would be appreciated.
You're on the right track with CABasicAnimation, as this is what is used for the animated user location annotations in the SDK. You can see this around here:
https://github.com/mapbox/mapbox-ios-sdk/blob/509fa7df46ebd654d130ab2f530a8e380bf2bd59/MapView/Map/RMMapView.m#L3593
Could you elaborate on this?
This is working well, except that my callout view is also blinking as it shares the same layer (and Mapbox messes with the animation during the tracking mode, resulting in an incorrect orientation as it is reseted with every move of the user anyway).
You do bring up a good point that the callout is a sublayer, so it would also blink. This use case hasn't been considered — is it possible for you to deselect your annotation and thus hide the callout?
But what is meant by the latter part?
I am currently animating a view from left to right. However, I need to stop the animation mid-flight. To do this I use:
CGRect frame = [movingView layer] presentationLayer] frame];
[movingView.layer removeAllAnimations];
movingView.frame = frame;
Unfortunately the view appears to jump slightly to the left when this code is executed. How can I stop the animation in its exact place?
Image showing what I would like to achieve
I want to move a view animately from a position to a new postion. If update the layer position of a view, I think the view will animately move to the new position, which is implicit animation I guess, but there's no animation, why?
- (IBAction)startAnimation:(id)sender {
CGPoint position = self.imageView.layer.position;
position.y += 90;
self.imageView.layer.position = position;
}
Implicit animations only happen for standalone layer, not layers backing up views. You will need to explicitly animate the position. You can either do it by creating a CABasicAnimation for the position and add it to the layer or use UIView animations to animate the center property of the view.
Creating an explicit animation
CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:#"position"];
move.toValue = [NSValue valueWithCGPoint:newPoint];
move.duration = 0.3;
[self.imageView.layer addAnimation:move forKey:#"myMoveAnimation"];
Using UIView animations
[UIView animateWithDuration:0.3 animations:^{
self.imageView.center = newCenter;
}];
I have a UIView with a shadow and a UIImageView subview.
I want to resize the view when the iPad is rotated and I'm trying to do this in the willRotateToInterfaceOrientation callback.
If I set the shadow on the UIView in the basic way the rotation is very choppy; so I'd like some suggestions from others on how to set the shadow setting layer.shadowPath.
I have tried animating the frame size change using [UIView animateWithDuration:animations] and setting the new shadowPath in the same block, but the shadow path snaps to the new size.
And if I don't change the layer's shadowPath in the animations block, it doesn't change.
From a few searches I've done, animating changes to layer properties need to be done with a CABasicAnimation.
So I think the question may be "how do I animate a UIView's frame size and layer change simultaneously?"
There's a bit more code than one would hope but something like this should work.
CGFloat animationDuration = 5.0;
// Create the CABasicAnimation for the shadow
CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:#"shadowPath"];
shadowAnimation.duration = animationDuration;
shadowAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; // Match the easing of the UIView block animation
shadowAnimation.fromValue = (id)self.shadowedView.layer.shadowPath;
// Animate the frame change on the view
[UIView animateWithDuration:animationDuration
delay:0.0f
options:UIViewAnimationCurveEaseInOut
animations:^{
self.shadowedView.frame = CGRectMake(self.shadowedView.frame.origin.x,
self.shadowedView.frame.origin.y,
self.shadowedView.frame.size.width * 2.,
self.shadowedView.frame.size.height * 2);
} completion:nil];
// Set the toValue for the animation to the new frame of the view
shadowAnimation.toValue = (id)[UIBezierPath bezierPathWithRect:self.shadowedView.bounds].CGPath;
// Add the shadow path animation
[self.shadowedView.layer addAnimation:shadowAnimation forKey:#"shadowPath"];
// Set the new shadow path
self.shadowedView.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.shadowedView.bounds].CGPath;
I have a very simple UIView animation, which causes my view to "throb":
[UIView animateWithDuration:0.2 delay:0.0f options:UIViewAnimationOptionAllowUserInteraction+UIViewAnimationOptionRepeat+UIViewAnimationOptionAutoreverse animations:^{
CGAffineTransform transf = CGAffineTransformScale(self.view.transform, 1.05f, 1.05f);
[self.view setTransform:transf];
} completion:nil];
At some point the user hits a button to cancel the animation, and apply a new transform.
[self.view.layer removeAllAnimations];
[self.view setTransform:aNewTransform];
I'd like it to reset to it's original transform, but instead it's getting increased in size by 5%.
Edit: I tried adding a completion block that resets the transform to it's original position. This works, but causes the transform I run immediately after to be trampled... the completion block gets run AFTER I apply aNewTransform.
Edit 2: I found a solution, using CABasicAnimation, instead of UIView animations. I would still be interested if anybody found a solution using UIView animations... I like the block-based interface better. This also only works, because I happen to be keeping track of my scale value separate from the one applied to the view. Everything that changes the scale uses a method that also changes self.scale
My replacement animation:
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
basicAnimation.toValue = [NSNumber numberWithFloat:self.scale*1.05f];
basicAnimation.fromValue = [NSNumber numberWithFloat:self.scale];
basicAnimation.autoreverses = YES;
basicAnimation.duration = 0.2;
basicAnimation.repeatCount = HUGE_VALF;
[self.view.layer addAnimation:basicAnimation forKey:#"Throb"];
Before starting a new animation on an existing view, you can reset the view if any of these attributes were previously altered:
// first, don't forget to stop ongoing animations for the view
[theView.layer removeAllAnimations];
// if the view was hidden
theView.hidden = NO;
// if you applied a transformation e.g. translate, scale, rotate..., reset to identity
theView.transform = CGAffineTransformIdentity;
// if you changed the anchor point, this will reset it to the center of the view
theView.layer.anchorPoint = CGPointMake(0.5, 0.5);
// if you changed the alpha, this will reset it to visible
theView.alpha = 1.;
Try putting the line
[self.view setTransform:aNewTransform];
in the completion block.