I am using this code to create the slide to unlock like animation but I am unable to make it animate from right to left.
How can I make it start from right and animate to left?
-(void)slideToCancel {
CALayer *maskLayer = [CALayer layer];
// Mask image ends with 0.15 opacity on both sides. Set the background color of the layer
// to the same value so the layer can extend the mask image.
maskLayer.backgroundColor = [[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.50f] CGColor];
maskLayer.contents = (id)[[UIImage imageNamed:#"slidetocancel.png"] CGImage];
// Center the mask image on twice the width of the text layer, so it starts to the left
// of the text layer and moves to its right when we translate it by width.
maskLayer.contentsGravity = kCAGravityCenter;
maskLayer.frame = CGRectMake(_slideToCancelLbl.frame.size.width * -1, 0.0f, _slideToCancelLbl.frame.size.width * 2, _slideToCancelLbl.frame.size.height);
// Animate the mask layer's horizontal position
CABasicAnimation *maskAnim = [CABasicAnimation animationWithKeyPath:#"position.x"];
maskAnim.byValue = [NSNumber numberWithFloat:_slideToCancelLbl.frame.size.width];
maskAnim.repeatCount = 1e100f;
maskAnim.duration = 1.5f;
[maskLayer addAnimation:maskAnim forKey:#"slideAnim"];
_slideToCancelLbl.layer.mask = maskLayer;
}
You should be able to simply use a negative byValue. However, the code you've posted is a canned animation, not one that responds to a slide gesture.
Related
I need help with core animation on iOS
I have the following view
I need to animate curve appearance from left to right. This step was achieved by animationWithKeyPath:#"strokeEnd", it works and all is fine.
Also I need to animate the gradient appearance under the curve, which goes from left to right along with the curve appearance
Here is the cut from reference video about the appearance I need to achieve
How can I achieve this?
Layers and paths I have:
(1) gradient curve line - uishapelayer
colored gradient - cagradient layer having (1) as mask
(2) closed path layer - path along the (1) curve, but with [path closePath];
gray gradient - cagradient having (2) as mask
(3) animationLayer - layer, added as [self.layer addSublayer:animationLayer]
(3) has colored gradien and gray gradient as sublayers
initWithFrame:
_animationLayer = [CALayer new];
_animationLayer.frame = frame;
_animationLayer.masksToBounds = YES;
[self.layer addSublayer:_animationLayer];
_path = [self characteristicsGraph:frame];
_pathLayer = [CAShapeLayer new];
_pathLayer.frame = frame;
_pathLayer.path = _path.CGPath;
_pathLayer.strokeColor = [color CGColor];
_pathLayer.fillColor = nil;
_pathLayer.lineWidth = lineW;
_fillPathLayer = [CAShapeLayer new];
_fillPathLayer.frame = frame;
_fillPathLayer.path = [self closePath:_path].CGPath;
_fillPathLayer.strokeColor = [color CGColor];
_fillPathLayer.fillColor = [color CGColor];
_fillPathLayer.masksToBounds = YES;
_fillGradient = [self gradient:frame];
_fillGradient.frame = frame;
_fillGradient.mask = _fillPathLayer;
_lineGradient = [self lineGradient:frame];
_lineGradient.frame = frame;
_lineGradient.mask = _pathLayer;
[_animationLayer addSublayer:_fillGradient];
[_animationLayer addSublayer:_lineGradient];
pathLayer animation (animates colored gradient appearance):
self.currentAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
self.currentAnimation.fromValue = #(0);
self.currentAnimation.toValue = #(1);
self.currentAnimation.duration = .5;
[self.pathLayer addAnimation:self.currentAnimation forKey:animationKey];
Gray gradient is static, can't animate it.
Will be grateful for help
You could make a mask layer for the gradient layer - just make it a rectangle that expands as the line animates.
The other problem I noticed if you want to be 100% true to the video is that your gradient will need to be angled along the trend of the chart - notice their chart trends up, and the gradient angle matches.
I am trying to create a circle which is filled depending upon a certain percentage. Here is pic of the effect I am going for:
I have a UIView and to this I add a CAShapeLayer which draws the circle. I am then creating another UIShapeLayer as a square to match the UIView containing the circle. Then setting the height of this depending on the figure. So if the square is 100px high and the value is 10% then I set the square to 10px so that it fills 10% of circle.
The image below shows 50% of the circle being filled. As you can see it covers the UIView as well as the circle. However when I try to set the CAShapeLayer to mask to bounds ( circle.maskToBounds) the circle complete disappears along with the square that I am adding as it's subview.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
[self drawCircleInView:view];
[self.view addSubview:view];
}
- (void)drawCircleInView:(UIView *)v
{
// Set up the shape of the circle
int radius = v.frame.size.height / 2;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2*radius, 2*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(v.bounds)-radius,
CGRectGetMidY(v.bounds)-radius);
// Configure the apperence of the circle
circle.fillColor = [UIColor blueColor].CGColor;
circle.strokeColor = [UIColor clearColor].CGColor;
circle.lineWidth = 5;
circle.masksToBounds = YES; // HERE IS THE LINE OF CODE THAT MAKES THE CIRCLE DISAPPEAR
CAShapeLayer *sublayer = [CAShapeLayer layer];
sublayer.backgroundColor = [UIColor orangeColor].CGColor;
sublayer.opacity = .5f;
sublayer.frame = CGRectMake(0, 100, 200, 100);
[circle addSublayer:sublayer];
// Add to parent layer
[v.layer addSublayer:circle];
}
I am wondering why setting circle.masksToBounds = YES is making the circle and the sublayer disappear completely. My understanding is that by setting this it should only show the sublayer over the circle.
Many thanks in advance.
Try this:
- (void)drawCircleInView:(UIView *)v {
// Set up the shape of the circle
CGSize size = v.bounds.size;
CALayer *layer = [CALayer layer];
layer.frame = v.bounds;
layer.backgroundColor = [UIColor blueColor].CGColor;
CALayer *sublayer = [CALayer layer];
sublayer.frame = CGRectMake(0, size.height/2, size.width, size.height/2);
sublayer.backgroundColor = [UIColor orangeColor].CGColor;
sublayer.opacity = .5f;
CAShapeLayer *mask = [CAShapeLayer layer];
mask.frame = v.bounds;
mask.path = [UIBezierPath bezierPathWithOvalInRect:v.bounds].CGPath;
mask.fillColor = [UIColor blackColor].CGColor;
mask.strokeColor = [UIColor clearColor].CGColor;
[layer addSublayer:sublayer];
[layer setMask:mask];
// Add to parent layer
[v.layer addSublayer:layer];
}
maskToBounds is "to bounds" you are not setting bounds nor frame.
It's not necessary, but you'd better to set frame of CAShapeLayer, to prevent unnecessary confusion.
bounds is rectangle, not CAShapeLayer's shape. Use mask layer to mask by shape instead.
circle and sublayer is not need to be CAShapeLayer because it will be masked by shaped mask layer.
To achieve this use a square image with its center circle as transparent. like this image
How to use this image for this effect -
Add a square layer (with lighter color/ or non fill color) first. above that add a layer with the fill color with sane rect. and above all place this image. and change the frame of middle layer that has the fill color to achieve desired effect
I have a CABasicAnimation that creates an iris wipe effect on an image. In short, the animation works fine on the simulator but there is no joy on device. The timers still fire correctly and the animationCompleted block gets called however there is no visible animation.
Here is the code to get the iris wipe working:
- (void)irisWipe
{
animationCompletionBlock theBlock;
_resultsImage.hidden = FALSE;//Show the image view
[_resultsImage setImage:[UIImage imageNamed:#"logoNoBoarder"]];
[_resultsImage setBackgroundColor:[UIColor clearColor]];
[_resultsImage setFrame:_imageView.bounds];
//Create a shape layer that we will use as a mask for the waretoLogoLarge image view
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGFloat maskHeight = _resultsImage.layer.bounds.size.height;
CGFloat maskWidth = _resultsImage.layer.bounds.size.width;
CGPoint centerPoint;
centerPoint = CGPointMake( maskWidth/2, maskHeight/2);
//Make the radius of our arc large enough to reach into the corners of the image view.
CGFloat radius = sqrtf(maskWidth * maskWidth + maskHeight * maskHeight)/2;
// CGFloat radius = MIN(maskWidth, maskHeight)/2;
//Don't fill the path, but stroke it in black.
maskLayer.fillColor = [[UIColor clearColor] CGColor];
maskLayer.strokeColor = [[UIColor blackColor] CGColor];
maskLayer.lineWidth = radius; //Make the line thick enough to completely fill the circle we're drawing
// maskLayer.lineWidth = 10; //Make the line thick enough to completely fill the circle we're drawing
CGMutablePathRef arcPath = CGPathCreateMutable();
//Move to the starting point of the arc so there is no initial line connecting to the arc
CGPathMoveToPoint(arcPath, nil, centerPoint.x, centerPoint.y-radius/2);
//Create an arc at 1/2 our circle radius, with a line thickess of the full circle radius
CGPathAddArc(arcPath,
nil,
centerPoint.x,
centerPoint.y,
radius/2,
3*M_PI/2,
-M_PI/2,
NO);
maskLayer.path = arcPath;
//Start with an empty mask path (draw 0% of the arc)
maskLayer.strokeEnd = 1.0;
CFRelease(arcPath);
//Install the mask layer into out image view's layer.
_resultsImage.layer.mask = maskLayer;
//Set our mask layer's frame to the parent layer's bounds.
_resultsImage.layer.mask.frame = _resultsImage.layer.bounds;
//Create an animation that increases the stroke length to 1, then reverses it back to zero.
CABasicAnimation *swipe = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
swipe.duration = 1;
swipe.delegate = self;
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
swipe.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
swipe.fillMode = kCAFillModeForwards;
swipe.removedOnCompletion = NO;
swipe.autoreverses = NO;
swipe.toValue = [NSNumber numberWithFloat: 0];
//Set up a completion block that will be called once the animation is completed.
theBlock = ^void(void)
{
NSLog(#"completed");
};
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
// doingMaskAnimation = TRUE;
[maskLayer addAnimation: swipe forKey: #"strokeEnd"];
}
Is there something in iOS7 I should be aware of when working with CAAnimations etc? OR is there a error in the code?
Note this code was sourced from: How do you achieve a "clock wipe"/ radial wipe effect in iOS?
I think the problem (or at least part of it) may be this line:
[_resultsImage setFrame:_imageView.bounds];
That should read
[_resultsImage setBounds:_imageView.bounds];
Instead. If you set the FRAME to the bounds of the image view, you're going to move the image view to 0.0 in its superview.
I'm also not clear what _imageView is, as opposed to _resultsImage.
I would step through your code in the debugger, looking at the frame rectangles that are being set for the image view and mask layer, and all the other values that are calculated.
I make two images: one with my spinner image and another with mask image (partially transparent gradient). Imagine this: spinner image is rotating clockwise, in the bottom there is dark gradient (look like haze), rotated spinner partially hided with this gradient. That is my aim.
I add two UIImageView to my scene, but my mask image also influence to background colour and I don't want that. I want that my mask image masking only spinner, so I make this:
- (void)viewDidLoad
{
[super viewDidLoad];
//add mask to UIImageView
CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:#"mask.png"] CGImage];
mask.frame = CGRectMake(0, 0, self.spinner.bounds.size.width,self.spinner.bounds.size.height);
self.spinner.layer.mask = mask;
self.spinner.layer.masksToBounds = YES;
//rotate UIImageView (trouble with mask, it must not be rotated)
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 20.0f;
animation.repeatCount = INFINITY;
[self.spinner.layer addAnimation:animation forKey:#"Spinner"];
}
As a result I got I spinner with nice mask image, but my mask rotates with my spinner, I want to always clip my mask to bottom of my spinner UIImageView. How can I do this? Or my conception is wrong, if so, give me some directions how can I archive what I want. Feel free to ask. Thanks.
Was tricky for me, but I made it.
Here is what you need to make this: you need a UIView (as a container for UIImageView).
Change this part in source code that I posted below:
CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:#"mask.png"] CGImage];
mask.frame = self.yourContainerView.bounds;
self.yourContainerView.layer.mask = mask;
self.yourContainerView.layer.masksToBounds = YES;
That's all. Hope this will help somebody.
I was wondering how do someone achieve the "slide to unlock effect" on a UILabel's text without using a static image as previously asked here.
// I'd like to use the uilable's current text to this sample code but not seem to be able to do it.
// ---> UIImage *textImage = [UIImage imageNamed:#"SlideToUnlock.png"];
CGFloat textWidth = textImage.size.width;
CGFloat textHeight = textImage.size.height;
CALayer *textLayer = [CALayer layer];
textLayer.contents = (id)[textImage CGImage];
textLayer.frame = CGRectMake(10.0f, 215.0f, textWidth, textHeight);
CALayer *maskLayer = [CALayer layer];
// Mask image ends with 0.15 opacity on both sides. Set the background color of the layer
// to the same value so the layer can extend the mask image.
maskLayer.backgroundColor = [[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.15f] CGColor];
maskLayer.contents = (id)[[UIImage imageNamed:#"Mask.png"] CGImage];
// Center the mask image on twice the width of the text layer, so it starts to the left
// of the text layer and moves to its right when we translate it by width.
maskLayer.contentsGravity = kCAGravityCenter;
maskLayer.frame = CGRectMake(-textWidth, 0.0f, textWidth * 2, textHeight);
// Animate the mask layer's horizontal position
CABasicAnimation *maskAnim = [CABasicAnimation animationWithKeyPath:#"position.x"];
maskAnim.byValue = [NSNumber numberWithFloat:textWidth];
maskAnim.repeatCount = 1e100f;
maskAnim.duration = 1.0f;
[maskLayer addAnimation:maskAnim forKey:#"slideAnim"];
textLayer.mask = maskLayer;
[self.view.layer addSublayer:textLayer];
Thanks
You can use the below Project. It is a UILabel with slide to unlock animation
http://code4app.net/ios/Animated-Label/505fd71a6803fa1077000001
Top: UILabel with opaque background and clear text
Clear text is rendered in drawRect: func through complicated masking process
Middle: Worker View that is performing a repeating animation moving an image behind the top label
Bottom: a UIView that you add the middle and top subview to in that order. Can be whatever color you want the text to be
An example can be seen here https://github.com/jhurray/AnimatedLabelExample