CAEmitterLayer animation that leaves accurate trail along the path - ios

I need to leave a trail with UIView that is animated with CAKeyframeAnimation
Ball * ball = [[Ball alloc]init];
// customized the ball
CGMutablePathRef path = CGPathCreateMutable();
// filling in the path from points.
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = path;
anim.rotationMode = kCAAnimationRotateAuto;
anim.repeatCount = HUGE_VALF;
anim.duration = 1.2;
anim.cumulative = YES;
anim.additive = YES;
[ball.layer addAnimation:anim forKey:#"fly"];
Now the Ball.m will have the following Emitter layer:
#implementation TennisBall{
__weak CAEmitterLayer *emitterLayer;
}
+ (Class)layerClass
{
return [CAEmitterLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupLayer];
emitterLayer = [CAEmitterLayer layer];
emitterLayer.frame = self.bounds;
emitterLayer.emitterPosition = CGPointMake(160, 240);
emitterLayer.emitterSize = CGSizeMake(10, 10);
emitterLayer.renderMode = kCAEmitterLayerAdditive;
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.birthRate = 1;
emitterCell.lifetime = 10.0;
emitterCell.lifetimeRange = 0.5;
emitterCell.velocity = 20;
emitterCell.velocityRange = 10;
emitterCell.emissionRange = 0;
emitterCell.scaleSpeed = 0.3;
emitterCell.spin = 0;
emitterCell.color = [[UIColor colorWithRed:0.8 green:0.4 blue:0.2 alpha:0.1]
CGColor];
emitterCell.contents = (id)[[UIImage imageNamed:#"first.png"] CGImage];
[emitterCell setName:#"fire"];
emitterLayer.emitterCells = [NSArray arrayWithObject:emitterCell];
[self.layer addSublayer:emitterLayer];
}
return self;
}
I just need the ball to leave a trail that repeats the path that the ball travels. How do I achieve that?

To make sure the emitter position is correct it the ball layer,
You need to make sure emitterPosition same as its anchorPoint.
For example, if anchorPoint is (0,0), then emitterPosition is (0,0).
If anchorPoint is (0.5,0.5), and size is (0,0,300,300) then emitterPosition is (150,150)

Related

CAKeyframeAnimation animation starts without the first frame(path)

I want to animate the mask with different bezier shapes. My animation starts but I cannot see the first path. The code is below:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_shapeLayer = [CAShapeLayer layer];
[self drawParentLayer];
}
- (void)drawParentLayer {
_shapeLayer.frame = CGRectMake(0, 0, 350, 500);
_shapeLayer.lineWidth = 3;
_shapeLayer.strokeColor = [UIColor blackColor].CGColor;
_shapeLayer.fillColor = UIColor.whiteColor.CGColor;
_shapeLayer.path = [UIBezierPath bezierPathWithRect:_shapeLayer.bounds].CGPath;
_shapeLayer.masksToBounds = NO;
[self.view.layer addSublayer:_shapeLayer];
}
Then I create my mask layer but in the location of touch.
How to get touch point:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
CGPoint locationInLayer = CGPointMake(location.x - _shapeLayer.frame.origin.x, location.y - _shapeLayer.frame.origin.y);
_shapeLayer.fillColor = UIColor.yellowColor.CGColor;
[self createBezierLayerInPoint:locationInLayer];
}
Bezier layer in point of touch:
- (void)createBezierLayerInPoint:(CGPoint)point {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = UIColor.whiteColor.CGColor;
layer.shadowColor = [[UIColor whiteColor] CGColor];
layer.shadowOffset = CGSizeMake(0.0, 0.0);
layer.shadowOpacity = 0.8;
layer.shadowRadius = 20.0;
layer.lineWidth = 0;
UIBezierPath *bezier = [BezierObject createBezierObject1];
layer.bounds = bezier.bounds;
layer.path = bezier.CGPath;
layer.position = point;
// my bezier objects are big so I decided to scale
layer.transform = CATransform3DMakeScale(0.2, 0.2, 1);
_shapeLayer.mask = layer;
[self animate:layer];
}
The pictures from bezier1 to bezier3 are next:
Animation of mask(bezier) layer:
- (void)animate:(CAShapeLayer *)layer {
CAKeyframeAnimation *frameA = [CAKeyframeAnimation animation];
[frameA setKeyPath:#"path"];
frameA.values = #[(id)[BezierObject createBezierObject1].CGPath, (id)[BezierObject createBezierObject2].CGPath, (id)[BezierObject createBezierObject3].CGPath];
frameA.keyTimes = #[#0, #(1/3), #1];
frameA.additive = YES;
frameA.removedOnCompletion = NO;
frameA.fillMode = kCAFillModeForwards;
frameA.duration = 3;
[layer addAnimation:frameA forKey:#"path"];
}
As I mentioned before animation starts from the object2. So, maybe it's just simple mistake but I can't see it.
Thank you!
The problem is the second value in your keyTimes array. Since nominator and denominator are integers, the result is zero. You should force a floating point division like this:
frameA.keyTimes = #[#0.0, #(1.0/3.0), #1.0];

Multiple CALayer masks causing performance issues

I am trying to create a fairly simple animation using 6 CALayer objects, each masked by a path. However, I am running into significant lag spikes when trying to animate them. Here is a video of the animation running. I am able to boost performance by setting shouldRasterize to YES, however it results in pixelation of the text, as you can see from this image:
I can correct the pixelation by setting the rasterizationScale to the screen scale, however that brings back the lag spikes that occurred without rasterization!
Here is my code:
#interface splashLayer : CALayer
#end
#implementation splashLayer {
UIColor* color;
CALayer* l1, *l2, *l3, *l4, *l5, *l6;
CAShapeLayer* m1, *m2, *m3, *m4, *m5, *m6;
NSUInteger i;
}
-(instancetype) init {
if (self = [super init]) {
color = [lzyColors purpleColor];
i = 0;
m1 = [CAShapeLayer layer]; m2 = [CAShapeLayer layer]; m3 = [CAShapeLayer layer]; m4 = [CAShapeLayer layer]; m5 = [CAShapeLayer layer]; m6 = [CAShapeLayer layer];
self.shouldRasterize = YES;
self.rasterizationScale = screenScale(); // Slows down performance, but stops ugly pixelation.
CGMutablePathRef p = CGPathCreateMutable();
CGFloat const meanScreenLength = (screenHeight()+screenWidth())*0.5;
CGFloat const pythag = lzyMathsPythag(meanScreenLength, meanScreenLength);
CGFloat const halfPythag = pythag*0.5;
CGFloat const pythagHalfPythag = lzyMathsPythag(halfPythag, halfPythag);
CGPoint const center = screenCenter();
CGPoint p1 = {center.x, center.y-pythagHalfPythag};
CGPoint p2 = {center.x+pythagHalfPythag, center.y};
CGPoint p3 = {center.x, center.y+pythagHalfPythag};
CGPoint p4 = {center.x-pythagHalfPythag, center.y};
CGPathMoveToPoint(p, nil, p1.x, p1.y);
lzyCGPathAddLineToPath(p, p2);
lzyCGPathAddLineToPath(p, p3);
lzyCGPathAddLineToPath(p, p4);
CGPathCloseSubpath(p);
m1.path = p; m2.path = p; m3.path = p; m4.path = p; m5.path = p; m6.path = p;
CGPathRelease(p);
m1.position = (CGPoint){-pythag, -pythag}; m2.position = (CGPoint){-pythag, -pythag}; m3.position = (CGPoint){-pythag, -pythag};
m4.position = (CGPoint){pythag, pythag}; m5.position = (CGPoint){pythag, pythag}; m6.position = (CGPoint){pythag, pythag};
l1 = [CALayer layer];
l1.contents = (__bridge id _Nullable)(colorImage([color lightenByValue:0.6], screenSize()).CGImage);
l1.frame = (CGRect){CGPointZero, screenSize()};
l1.mask = m1;
l2 = [CALayer layer];
l2.contents = (__bridge id _Nullable)(textBG([color lightenByValue:0.3], screenSize()).CGImage);
l2.frame = (CGRect){CGPointZero, screenSize()};
l2.mask = m2;
l3 = [CALayer layer];
// l3.rasterizationScale = screenScale(); (Doesn't work)
l3.contents = (__bridge id _Nullable)(textBG(color, screenSize()).CGImage);
l3.frame = (CGRect){CGPointZero, screenSize()};
l3.mask = m3;
UIColor* color2 = [lzyColors redColor];
l4 = [CALayer layer];
l4.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.6], screenSize()).CGImage);
l4.frame = (CGRect){CGPointZero, screenSize()};
l4.mask = m4;
l5 = [CALayer layer];
l5.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.3], screenSize()).CGImage);
l5.frame = (CGRect){CGPointZero, screenSize()};
l5.mask = m5;
l6 = [CALayer layer];
l6.contents = (__bridge id _Nullable)(colorImage(color2, screenSize()).CGImage);
l6.frame = (CGRect){CGPointZero, screenSize()};
l6.mask = m6;
[self addSublayer:l1]; [self addSublayer:l2]; [self addSublayer:l3]; [self addSublayer:l4]; [self addSublayer:l5]; [self addSublayer:l6];
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"position"];
anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){-pythag, -pythag}];
anim.toValue = [NSValue valueWithCGPoint:CGPointZero];
anim.delegate = self;
anim.beginTime = CACurrentMediaTime()+1;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.duration = 1;
[m1 addAnimation:anim forKey:#"0"];
anim.duration = 1.25;
[m2 addAnimation:anim forKey:#"1"];
anim.duration = 1.5;
[m3 addAnimation:anim forKey:#"2"];
anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){pythag, pythag}];
anim.beginTime = CACurrentMediaTime()+2.5;
anim.duration = 1;
[m4 addAnimation:anim forKey:#"3"];
anim.duration = 1.25;
[m5 addAnimation:anim forKey:#"4"];
anim.duration = 1.5;
[m6 addAnimation:anim forKey:#"5"];
}
return self;
}
#end
I know the code is very crude, but I've just simplified it for debugging.
The colorImage() & textBG() functions just do some Core Graphics rendering to produce the 6 images for the 6 layers. This shouldn't be the source of the problem as drawing is simple and the animation is delayed by a second before starting.
I tried only setting the rasterizationScale to the screen scale on layers that display text, but this didn't work.
I have also tried to improve performance by removing the two layers underneath the third layer, once it has finished animating, however it hasn't improved performance significantly.
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (flag) {
if (i == 2) {
[l1 removeFromSuperlayer];
l1 = nil;
m1 = nil;
[l2 removeFromSuperlayer];
l2 = nil;
m2 = nil;
l3.mask = nil;
m3 = nil;
}
i++;
}
}
Any suggestions on how to improve the performance?
I would suggest compositing your layers and masks into static images and animating those. That should be plenty fast.
Well, after trying countless methods to try and achieve this animation; I have arrived at the conclusion that I am probably better off just making the leap over to openGL. This will allow me to create a more general solution to the problem that'll fare better with more complex animations.

CAShapeLayer animation doesn't stay on screen but disappears

I'm trying to draw a animated circle but every segment needs to have another color. Now everything works except that my piece that is just drawed before I call the method again disappears so only the last part stays. I don't want that, I want that after 4 times a whole circle is drawn in different color strokes.
How can I fix this that it doesn't disappears?
This is my init code:
- (void)_initCircle {
_circle = [CAShapeLayer layer];
_radius = 100;
// Make a circular shape
_circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * _radius, 2.0 * _radius) cornerRadius:_radius].CGPath;
// Center the shape in self.view
_circle.position = CGPointMake(CGRectGetMidX(self.view.frame) - _radius,
CGRectGetMidY(self.view.frame) - _radius);
_circle.fillColor = [UIColor clearColor].CGColor;
_circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:_circle];
}
This is my draw piece:
- (void)_drawStrokeOnCircleFrom:(float)start to:(float)end withColor:(UIColor *)color {
// Configure the apperence of the circle
_circle.strokeColor = color.CGColor;
_circle.strokeStart = start;
_circle.strokeEnd = end;
[CATransaction begin];
// Configure animation
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 2.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = [NSNumber numberWithFloat:start];
drawAnimation.toValue = [NSNumber numberWithFloat:end];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[CATransaction setCompletionBlock:^{
NSLog(#"Complete animation");
_index++;
if(_index < _votes.count){
_startStroke = _endStroke;
_endStroke += [_votes[_index] floatValue] / _totalListens;
[self _drawStrokeOnCircleFrom:_startStroke to:_endStroke withColor:_colors[_index]];
}
}];
// Add the animation to the circle
[_circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
[CATransaction commit];
}
Try setting these values on your CABasicAnimation:
drawAnimation.removedOnCompletion = NO;
drawAnimation.fillMode = kCAFillModeForwards;
That worked for me using "kCAFillModeBoth", both statements are necessary:
//to keep it after finished
animation.removedOnCompletion = false;
animation.fillMode = kCAFillModeBoth;
I solved the problem with extra layers like this:
- (void)_drawStrokeOnCircle {
[self _initCircle];
[CATransaction begin];
for (NSUInteger i = 0; i < 4; i++)
{
CAShapeLayer* strokePart = [[CAShapeLayer alloc] init];
strokePart.fillColor = [[UIColor clearColor] CGColor];
strokePart.frame = _circle.bounds;
strokePart.path = _circle.path;
strokePart.lineCap = _circle.lineCap;
strokePart.lineWidth = _circle.lineWidth;
// Configure the apperence of the circle
strokePart.strokeColor = ((UIColor *)_colors[i]).CGColor;
strokePart.strokeStart = [_segmentsStart[i] floatValue];
strokePart.strokeEnd = [_segmentsEnd[i] floatValue];
[_circle addSublayer: strokePart];
// Configure animation
CAKeyframeAnimation* drawAnimation = [CAKeyframeAnimation animationWithKeyPath: #"strokeEnd"];
drawAnimation.duration = 4.0;
// Animate from no part of the stroke being drawn to the entire stroke being drawn
NSArray* times = #[ #(0.0),
#(strokePart.strokeStart),
#(strokePart.strokeEnd),
#(1.0) ];
NSArray* values = #[ #(strokePart.strokeStart),
#(strokePart.strokeStart),
#(strokePart.strokeEnd),
#(strokePart.strokeEnd) ];
drawAnimation.keyTimes = times;
drawAnimation.values = values;
drawAnimation.removedOnCompletion = NO;
drawAnimation.fillMode = kCAFillModeForwards;
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[strokePart addAnimation: drawAnimation forKey: #"drawCircleAnimation"];
}
[CATransaction commit];
}
- (void)_initCircle {
[_circle removeFromSuperlayer];
_circle = [CAShapeLayer layer];
_radius = 100;
// Make a circular shape
_circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * _radius, 2.0 * _radius) cornerRadius:_radius].CGPath;
// Center the shape in self.view
_circle.position = CGPointMake(CGRectGetMidX(self.view.frame) - _radius,
CGRectGetMidY(self.view.frame) - _radius);
_circle.fillColor = [UIColor clearColor].CGColor;
_circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:_circle];
}
SWIFT 4, Xcode 11:
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
let timeLeftShapeLayer = CAShapeLayer()
and in your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
drawTimeLeftShape()
strokeIt.fillMode = CAMediaTimingFillMode.forwards
strokeIt.isRemovedOnCompletion = false
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = 2
timeLeftShapeLayer.add(strokeIt, forKey: nil)
}
and by UIBezierPathcreate function to draw circle:
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(
arcCenter: CGPoint(x: myView.frame.midX , y: myView.frame.midY),
radius: myView.frame.width / 2,
startAngle: -90.degreesToRadians,
endAngle: 270.degreesToRadians,
clockwise: true
).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 1
timeLeftShapeLayer.strokeEnd = 0.0
view.layer.addSublayer(timeLeftShapeLayer)
}

iOS drag shape pinned to center

Taking a next step from this post Drag UIView around Shape Comprised of CGMutablePaths , I am trying to add a line which is on one end pinned to the center of the pathLayer_ and the other end gets dragged along with the circular object (circleLayer in handleView_) on the path (figure-8).
The line has its own layer andview and path initiated in (void)initHandleView.
First: I am not able to get the line to go through the center of the pathLayer_:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
}
- (void)initPathLayer {
pathLayer_ = [CAShapeLayer layer];
pathLayer_.lineWidth = 1;
pathLayer_.fillColor = nil;
pathLayer_.strokeColor = [UIColor lightGrayColor].CGColor;
pathLayer_.lineCap = kCALineCapButt;
pathLayer_.lineJoin = kCALineJoinRound;
pathLayer_.frame = self.view.bounds;
pathLayerCenter_ = CGPointMake(CGRectGetMidX(pathLayer_.frame), CGRectGetMidY(pathLayer_.frame));
[self.view.layer addSublayer:pathLayer_];
}
- (void)initHandleView {
handlePathPointIndex_ = 0;
CGRect rect = CGRectMake(0, 0, 30, 30);
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.fillColor = [UIColor redColor].CGColor;
circleLayer.strokeColor = [UIColor redColor].CGColor;
circleLayer.lineWidth = 2;
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(rect, circleLayer.lineWidth, circleLayer.lineWidth)];
circleLayer.frame = rect;
circleLayer.path = circlePath.CGPath;
handleView_ = [[UIView alloc] initWithFrame:rect];
CGRect lineRect = CGRectMake(0,0,CGRectGetMaxX([[UIScreen mainScreen] bounds]), CGRectGetMaxY([[UIScreen mainScreen] bounds]));
CGPoint center = CGPointMake(CGRectGetMidX(pathLayer_.frame), CGRectGetMidY(pathLayer_.frame));
lineView_ = [[UIView alloc] initWithFrame:lineRect];
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor blueColor].CGColor;
lineLayer.strokeColor = [UIColor blueColor].CGColor;
lineLayer.lineWidth = 2;
lineLayer.frame = self.view.bounds;
UIBezierPath *linePath = [UIBezierPath bezierPath];
CGPoint circlePoint = CGPointMake(handleView_.center.x, handleView_.center.y);
[linePath moveToPoint:center]; //Center
[linePath addLineToPoint:circlePoint];
[linePath moveToPoint:circlePoint]; //Handle
lineLayer.path = linePath.CGPath;
[handleView_.layer insertSublayer:circleLayer above:handleView_.layer];
[handleView_.layer insertSublayer:lineLayer above:handleView_.layer];
// handleView_.layer.masksToBounds = YES;
float direction = DEGREES_TO_RADIANS(10);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(direction);
[handleView_ setTransform:rotationTransform];
[self.view addSubview:lineView_];
[self.view addSubview:handleView_];
}
And second: I am not sure I do the right thing with the PanGesturerecognizer:
- (void)handleWasPanned:(UIPanGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
desiredHandleCenter_ = handleView_.center;
break;
}
case UIGestureRecognizerStateChanged:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
CGPoint translation = [recognizer translationInView:self.view];
desiredHandleCenter_.x += translation.x;
desiredHandleCenter_.y += translation.y;
[self moveHandleTowardPointAndRotateLine:desiredHandleCenter_];
break;
}
default:
break;
}
[recognizer setTranslation:CGPointZero inView:self.view];
}
- (void)moveHandleTowardPointAndRotateLine:(CGPoint)point {
CGFloat earlierDistance = [self distanceToPoint:point ifHandleMovesByOffset:-1];
CGFloat currentDistance = [self distanceToPoint:point ifHandleMovesByOffset:0];
CGFloat laterDistance = [self distanceToPoint:point ifHandleMovesByOffset:1];
if (currentDistance <= earlierDistance && currentDistance <= laterDistance)
return;
NSInteger step;
CGFloat distance;
if (earlierDistance < laterDistance) {
step = -1;
distance = earlierDistance;
} else {
step = 1;
distance = laterDistance;
}
NSInteger offset = step;
while (true) {
NSInteger nextOffset = offset + step;
CGFloat nextDistance = [self distanceToPoint:point ifHandleMovesByOffset:nextOffset];
if (nextDistance >= distance)
break;
distance = nextDistance;
offset = nextOffset;
}
handlePathPointIndex_ += offset;
// Make one end of the line move with handle (point) while the other is pinned to center of pathLayer_ (ie. in figure8 its the cross point, in a circle it's center)
//CGFloat rot = [self getTouchAngle:point];
CGFloat rot = atan2f((point.x - pathLayerCenter_.x), -(point.y - pathLayerCenter_.y));
handleView_.layer.transform = CATransform3DMakeRotation(rot, 0., 0., 1.);
[self layoutHandleView];
}
I'm just going to ignore your code and tell you how I'd modify my original answer to add the line you want.
First, I'm going to use a dedicated shape layer for the line, so I'll add an instance variable to reference the line layer:
#implementation ViewController {
CAShapeLayer *lineLayer_; // NEW!!!
UIBezierPath *path_;
CAShapeLayer *pathLayer_;
etc.
I need to initialize the line layer when I load the view:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
[self initLineLayer]; // NEW!!!
}
- (void)initLineLayer {
lineLayer_ = [CAShapeLayer layer];
lineLayer_.lineWidth = 1;
lineLayer_.fillColor = nil;
lineLayer_.strokeColor = [UIColor redColor].CGColor;
lineLayer_.lineCap = kCALineCapRound;
lineLayer_.lineJoin = kCALineJoinRound;
[self.view.layer addSublayer:lineLayer_];
}
I need to lay out the line layer when I lay out my top-level view (when it's first shown and on rotations):
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self createPath];
[self createPathPoints];
[self layoutPathLayer];
[self layoutHandleView];
[self layoutLineLayer]; // NEW!!!
}
- (void)layoutLineLayer {
lineLayer_.frame = self.view.bounds;
CGRect bounds = self.view.bounds;
CGPoint start = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint end = pathPoints_[handlePathPointIndex_];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:start];
[path addLineToPoint:end];
lineLayer_.path = path.CGPath;
}
Finally, I need to lay out the line layer again (to update its path) whenever I update the handle:
- (void)moveHandleTowardPoint:(CGPoint)point {
// I have omitted most of this method body for brevity.
// Just copy it from the original answer.
...
...
handlePathPointIndex_ += offset;
[self layoutHandleView];
[self layoutLineLayer]; // NEW!!!
}
Result:

iOS CAEmitterLayer how to show animation on uiimageview

I am following this tutorial "http://www.raywenderlich.com/6063/uikit-particle-systems-in-ios-5-tutorial". everything works but I want that effect to go on my image view.
please help where should I make changes
this is code I have in my view controller on which I am showing animation
-(IBAction)ClickOn:(id)sender
{
[fireView startAnimation];
[fireView setIsEmitting:YES];
[self.view bringSubviewToFront:fireView];
[self.view sendSubviewToBack:sample];
}
and this is code in library view class
-(void)startAnimation
{
NSLog(#"awakeFromNib");
//set ref to the layer
fireEmitter = (CAEmitterLayer*)self.layer; //2
//configure the emitter layer
fireEmitter.emitterSize = CGSizeMake(20, 20);
CAEmitterCell* fire = [CAEmitterCell emitterCell];
fire.birthRate = 0;
fire.lifetime = 2.0;
fire.lifetimeRange = 1.5;
fire.color = [[UIColor colorWithRed:236 green:237 blue:237 alpha:0.1] CGColor];
fire.contents = (id)[[UIImage imageNamed:#"Particles_fire.png"] CGImage];
[fire setName:#"fire"];
fire.yAcceleration =
fire.velocity = 0.1;
fire.velocityRange = 80;
fire.emissionRange = 80;
fire.scale = 1.0;
fire.scaleSpeed = 0.1;
fire.spin = 40.5;
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:#"emitterPosition"];
ba.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 400)];
ba.toValue = [NSValue valueWithCGPoint:CGPointMake(300,0)];
ba.duration = 6;
ba.autoreverses = NO;
[fireEmitter addAnimation:ba forKey:nil];
fireEmitter.renderMode = kCAEmitterLayerPoints;
//add the cell to the layer and we're done
fireEmitter.emitterCells = [NSArray arrayWithObject:fire];
fireEmitter.zPosition = 400.0;
}
basically i just want animation to go above uiimageview or uiview, currently it is going behind it

Resources