CAEmitterlayer crashing when birthrate over 5 in iOS 9 - objective-c

I'm using a particle emitter in an iPad project (not using SpriteKit), the particle emitter is generated inside a UIImageView and since iOS 9 this emitter is making the whole app crash. Actually, the whole iPad is crashing, the image freezes and nothing can make the iPad unfreeze short of restarting the iPad.
If I bring the birthrate down from 19.0f to 6.0f the iPad stops crashing.
The image I am using is a png of 256 × 256 in size, I'm not sure if that might play a role in it crashing.
Everything was working fine in iOS 8.
I'm currently on iOS 9.0.1 on an iPad Air 2, using Xcode 7.0.1.
+(void)smogGeneral:(UIImageView *)myView{
[myView setNeedsDisplay];
CALayer *rootLayer = [myView layer];
CALayer *emitterSuperLayer = [CALayer layer];
emitterSuperLayer.bounds = myView.bounds;
emitterSuperLayer.sublayerTransform = CATransform3DMakeScale(1.0f, -1.0f, 1.0f);
[rootLayer addSublayer:emitterSuperLayer];
CGFloat midX = floorf(CGRectGetMidX(myView.bounds));
CGFloat midY = floorf(CGRectGetMidY(myView.bounds));
[emitterSuperLayer setPosition:CGPointMake(midX, midY)];
CAEmitterLayer *whiteSmokeEmitter = [CAEmitterLayer layer];
[whiteSmokeEmitter setName:#"whiteSmokeEmitter"];
whiteSmokeEmitter.zPosition = 0.00f;
whiteSmokeEmitter.emitterPosition = CGPointMake(300, 300);
whiteSmokeEmitter.renderMode = kCAEmitterLayerBackToFront;
whiteSmokeEmitter.emitterSize = CGSizeMake(10.00f, 10.0f);
whiteSmokeEmitter.velocity = 0.05f;
whiteSmokeEmitter.emitterMode = kCAEmitterLayerOutline;
whiteSmokeEmitter.emitterShape = kCAEmitterLayerRectangle;
CAEmitterCell *puffCell = [CAEmitterCell emitterCell];
[puffCell setName:#"puffCell"];
puffCell.contents = (__bridge id)[[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"smokeCell" ofType:#"png"]] CGImage];;
CGColorRef colorRefPuffcell = [UIColor colorWithRed:0.99f green:0.98f blue:1.00f alpha:0.10f].CGColor;
puffCell.color = colorRefPuffcell;
puffCell.scale = 0.2f;
puffCell.emissionLongitude = 1.84f;
puffCell.emissionRange = 1.11f;
puffCell.lifetime = 16.0f;
puffCell.birthRate = 19.0f;
puffCell.scaleSpeed = 0.15f;
puffCell.velocity = 2.56f;
puffCell.velocityRange = 9.80f;
puffCell.xAcceleration = 9.15f;
puffCell.yAcceleration = -0.01f;
whiteSmokeEmitter.emitterCells = #[puffCell];
[emitterSuperLayer addSublayer:whiteSmokeEmitter];
}

Related

Bake lightprobes in Scenekit programmatically

I am facing issues baking lightprobes on iOS. The same logic bakes lightprobes on macOS successfully. iOS throws the following exception
[MTLDebugCommandBuffer waitUntilCompleted]:201: failed assertion `waitUntilCompleted on uncommitted command buffer'
Some specs : Runtime: iOS 15.4 - DeviceType: iPhone 13 Pro Max. Following is the code for lightprobe bake :
SCNScene* scene = [SCNScene scene];
SCNNode *ambientLight = [SCNNode node];
ambientLight.light = [SCNLight light];
ambientLight.light.type = SCNLightTypeAmbient;
ambientLight.light.color = [UIColor whiteColor];
ambientLight.light.intensity = 1000.0;
[scene.rootNode addChildNode:ambientLight];
scene.background.contents = [UIColor whiteColor];
scene.background.intensity = 2000.;
SCNNode *probe1 = [SCNNode node];
probe1.position = SCNVector3Make(-0.493530, 1.7285934, -0.150000);
probe1.light = [SCNLight light];
probe1.light.type = SCNLightTypeProbe;
[scene.rootNode addChildNode:probe1];
SCNRenderer* probeRenderer = [SCNRenderer rendererWithDevice:nil options:nil];
probeRenderer.scene = scene;
NSArray<SCNNode*> *probes = [NSArray arrayWithObjects: probe1, nil];
[probeRenderer updateProbes: probes atTime:1.0];
The crash occurs at updateProbes. Also, I have logged and checked the 27 floats and they are not garbage for macOS so essentially the bake is working as expected on macOS. Any help would be really appreciated!

Apply rounded corners to arc created with UIBezierPath

I am working on a circular progress bar that i've created using UIBezierPath. The progress bar looks like the following picture:
My question is: how to make the edge of the arc rounded and not rectangular?
The code i used to draw the arc looks like is:
// Draw the arc with bezier path
int radius = 100;
arc = [CAShapeLayer layer];
arc.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:M_PI endAngle:M_PI/150 clockwise:YES].CGPath;
arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
arc.fillColor = [UIColor clearColor].CGColor;
arc.strokeColor = [UIColor purpleColor].CGColor;
arc.cornerRadius = 0.5;
arc.lineWidth = 6;
[self.view.layer addSublayer:arc];
// Animation of the progress bar
drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = YES; // Remain stroked after the animation..
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:10.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[arc addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
I tried to use arc.cornerRadius but it seemed to return nothing.
Any help would be appreciated.
Thank you in advance!
Granit
Set lineCapStyle to kCGLineCapRound (On the bezier path) to draw the ends of the lines with a round edge.
You can also set the lineCap on the shape layer to do the same thing.
To make corner as round you can use this.
arc.lineCap = #"round";
The swift equivalent is:
let circlePath = UIBezierPath(…)
circlePath.lineCapStyle = .round

Circular-slider weird behaviour while animating with CA

I wanted to make a circular Slider which is draggable and animatable. So far I've managed to build the slider and use the drag handle to move it around and even animate it. Sometimes animation goes wrong (wrong direction or shortest direction. I've subclassed a UIView (Will be a UIControl soon, just wanted to get the animation right first) added a PanGestureRecognizer and several layers for the drawing.
So how do I fix this weird behaviour? I've someone could help me here, I'd be thankful :)
Here's the sample project -> http://cl.ly/2l0O3b1I3U0X
Thanks a lot!
EDIT:
Here's the drawing code:
CALayer *aLayer = [CALayer layer];
aLayer.bounds = CGRectMake(0, 0, 170, 170);
aLayer.position = self.center;
aLayer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
self.handleHostLayer = [CALayer layer];
self.handleHostLayer.bounds = CGRectMake(0, 0, 170, 170);
self.handleHostLayer.position = CGPointMake(CGRectGetMaxX(aLayer.bounds) - 170/2.0, CGRectGetMaxY(aLayer.bounds) - 170/2.0);
[aLayer addSublayer:self.handleHostLayer];
[self.layer addSublayer:aLayer];
self.handle = [CALayer layer];
self.handle.bounds = CGRectMake(0, 0, 50, 50);
self.handle.cornerRadius = 25;
self.handle.backgroundColor = [UIColor whiteColor].CGColor;
self.handle.masksToBounds = NO;
self.handle.shadowOffset = CGSizeMake(3.0, 0.0);
self.handle.shadowRadius = 0;
self.handle.shadowOpacity = .15;
self.handle.shadowColor = [UIColor blackColor].CGColor;
[self.handleHostLayer addSublayer:self.self.handle];
Here's the animation code:
CGFloat handleTarget = ToRad(DEG);
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotationAnimation.fromValue = #([[self.handleHostLayer valueForKeyPath:#"transform.rotation"] floatValue]);
rotationAnimation.toValue = #(handleTarget);
rotationAnimation.duration = .5;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.cumulative = YES;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[self.handleHostLayer addAnimation:rotationAnimation forKey:#"transform.rotation"];
OK, I looked at your project. Your problem is that the to and from angles don't both fall in the 0≤ɸ<2π range.
You can make sure that they do by adding and removing 2π until they both are within that range.
CGFloat fromAngle = [[self.handleHostLayer valueForKeyPath:#"transform.rotation"] floatValue];
CGFloat toAngle = handleTarget;
while (fromAngle >= 2.0*M_PI) { fromAngle -= 2*M_PI; }
while (toAngle >= 2.0*M_PI) { toAngle -= 2*M_PI; }
while (fromAngle < 0.0) { fromAngle += 2*M_PI; }
while (toAngle < 0.0) { toAngle += 2*M_PI; }
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotationAnimation.fromValue = #(fromAngle);
rotationAnimation.toValue = #(toAngle);
// As before...
Another things you could change is the misuse of removeOnCompletion. I did a long explanation in this answer about why it's bad to do so.
In short: you are not animating from the value that you think you are animating since you inadvertently introduce a difference between the value of the layers property and what you see on screen.
Skip that line all together. You can also skip the skip the cumulative line since you are not repeating the animation. Now, if your animations doesn't stick: set the model value to its final value before adding the animation to it.

CABasicAnimation - Infinite scrolling of an image on retina device is garbled

In a similar post, I found a solution to animating a scrolling image infinitely:
Animate infinite scrolling of an image in a seamless loop
While this solution works beautifully, there seems to be an issue when running on a retina device. In particular, I am running this project on an iPad only. A non-retina iPad 2 scrolls the image without any problems. But run on a retina iPad 3 or 4 the image is a mess! It's difficult to describe, but the best I can say is that it is garbled. Pixels are stretched every which way. It resembles a Jackson Pollak painting.
Screen shot:
http://imgur.com/Q7n08kv
I tested this using a non-retina image (non 2x) and a retina version (#2x). The image is large - full screen (landscape) and is 4 panels wide (4096 x 768). I played around with smaller images but with the same result.
Is there a limitation with the scrolling functionality of CABasicAnimation that would affect retina devices? Here is the code I am using (as contributed by rob mayoff):
UIImage *crawlImage = [UIImage imageNamed:#"CrawlBackground.png"];
UIColor *crawlPattern = [UIColor colorWithPatternImage:crawlImage];
self.crawlLayer = [CALayer layer];
self.crawlLayer.backgroundColor = crawlPattern.CGColor;
self.crawlLayer.transform = CATransform3DMakeScale(1, -1, 1);
self.crawlLayer.anchorPoint = CGPointMake(0, 1);
self.crawlLayer.frame = CGRectMake(0, 0, crawlImage.size.width + 1024, crawlImage.size.height);
[self.backgroundCrawl.layer addSublayer:self.crawlLayer];
self.backgroundCrawl.layer.zPosition = 0;
CGPoint startPoint = CGPointZero;
CGPoint endPoint = CGPointMake(-crawlImage.size.width, 0);
self.crawlLayerAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
self.crawlLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
self.crawlLayerAnimation.fromValue = [NSValue valueWithCGPoint:startPoint];
self.crawlLayerAnimation.toValue = [NSValue valueWithCGPoint:endPoint];
self.crawlLayerAnimation.repeatCount = HUGE_VALF;
self.crawlLayerAnimation.duration = 30; // nn seconds to complete the cycle
self.crawlLayer = [CALayer layer];
As soon as you make a layer you should set its contentsScale:
self.crawlLayer.contentsScale = [[UIScreen mainScreen] scale];
Otherwise it may not display correctly on double-resolution screens.
self.crawlLayer.contents = (id)crawlImage.CGImage;
or:
self.crawlLayer.contents = (__bridge id)([crawlImage CGImage]);
This should fix it. Allowing large images to scroll correctly.

image rotation, covers the button

I want to rotate the image around the x-axis from left to right. The problem is that when you rotate the image covers the button located on the top
Run animation
[AnimationUtil rotationRightToLeftForView:image andDuration:1];
Animation metod
+(void) rotationRightToLeftForView:(UIView *)flipView andDuration:(NSTimeInterval)duration{
// Remove existing animations before stating new animation
[flipView.layer removeAllAnimations];
// Make sure view is visible
flipView.hidden = NO;
// show 1/2 animation
//flipView.layer.doubleSided = NO;
// disable the view so it’s not doing anythign while animating
flipView.userInteractionEnabled = NO;
// Set the CALayer anchorPoint to the left edge and
// translate the button to account for the new
// anchorPoint. In case you want to reuse the animation
// for this button, we only do the translation and
// anchor point setting once.
if (flipView.layer.anchorPoint.x != 0.0f) {
flipView.layer.anchorPoint = CGPointMake(0.0f, 0.5f);
flipView.center = CGPointMake(flipView.center.x-flipView.bounds.size.width/2.0f, flipView.center.y);
}
// create an animation to hold the page turning
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
transformAnimation.removedOnCompletion = NO;
transformAnimation.duration = duration;
transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// start the animation from the current state
transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
// this is the basic rotation by 180 degree along the y-axis M_PI
CATransform3D endTransform = CATransform3DMakeRotation(radians(180.0), 0.0f, -1.0f, 0.0f);
transformAnimation.toValue = [NSValue valueWithCATransform3D:endTransform];
// Create an animation group to hold the rotation
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
// Set self as the delegate to receive notification when the animation finishes
theGroup.delegate = self;
theGroup.duration = duration;
// CAAnimation-objects support arbitrary Key-Value pairs, we add the UIView tag
// to identify the animation later when it finishes
[theGroup setValue:[NSNumber numberWithInt:flipView.tag] forKey:#"viewFlipTag"];
// Here you could add other animations to the array
theGroup.animations = [NSArray arrayWithObjects:transformAnimation, nil];
theGroup.removedOnCompletion = NO;
// Add the animation group to the layer
[flipView.layer addAnimation:theGroup forKey:#"flipView"];
}
The decision follows:
Create mask (image) Color = black, make region Size = Button.size Color = transparent
image name = "mask2.png"
button = ...;
ImageView = ...;
parent_view = ...;
UIImage *_maskingImage = [UIImage imageNamed:#"mask2"];
CALayer *_maskingLayer = [CALayer layer];
_maskingLayer.frame = parent_View.bounds;
[_maskingLayer setContents:(id)[_maskingImage CGImage]];
CALayer *layerForImage = [[CALayer alloc] init];
layerForImage.frame = parent_View.bounds;
[layerForImage setMask:_maskingLayer];
[layerForImage addSublayer:ImageView.layer];
[parent_View.layer addSublayer:layerForImage];
[parent_View addSubView:button];