Rotate a sprite in cocos2d using accelerometer - iphone

I'm on cocos2d and I have a sprite that I would like to rotate with the accelerometer.
I've heard about CMMotionManager. I would like to know if it is possible to use it just for 2D rotation, and, if yes, how?

put this in onEnter:
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.updateInterval = 1.0/50.0; //update interval in sec...so 1/50= 20 ms
accelerometer.delegate = self;
you need to conform to UIAccelerometerDelegate like so:
#interface MyClass:CCLayer <UIAccelerometerDelegate>
and implement this in MyClass.m:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
CCLOG(#"x = %f y = %f z = %f",acceleration.x,acceleration.y,acceleration.z);
mysprite.rotation=acceleration.x*20;
}
edit: almost forgot...put accelerometer.delegate = nil; in onExit
note that the method is called every time the accelerometer changes value..in all 3 vectors
cards on the table..i didnt use accelerometer ...ever..but it should look something like this...check the rotation property in the documentation and play a little with it
hope it helps
PS: loved the "sorry for my english i'm french" part...hilarious
edit: here is my test of that code..and made a few modifications..it works fairly smooth..if you dont like it play around with the values.
#import "cocos2d.h"
// HelloWorldLayer
UIAccelerationValue accelerationX;
UIAccelerationValue accelerationY;
float currentRawReading;
float calibrationOffset;
#interface HelloWorldLayer : CCLayer <UIAccelerometerDelegate>
{
CCLabelTTF *label;
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
#import "HelloWorldLayer.h"
// HelloWorldLayer implementation
#implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
#define kFilteringFactor .05
CGFloat RadiansToDegrees(CGFloat radians) {return radians *180/M_PI;};
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
UIAccelerometer *accel= [UIAccelerometer sharedAccelerometer];
accel.delegate=self;
accel.updateInterval=1/60;
// create and initialize a Label
label = [CCLabelTTF labelWithString:#"Hello World" fontName:#"Marker Felt" fontSize:64];
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );
label.flipY=YES; //i have absolutly no idea why the label is fliped :/
label.flipX=YES;
label.rotation=0;
// add the label as a child to this Layer
[self addChild: label];
}
return self;
}
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
CCLOG(#"acc called");
accelerationX=acceleration.x *kFilteringFactor +accelerationX *(1-kFilteringFactor);
accelerationY=acceleration.y*kFilteringFactor +accelerationY *(1-kFilteringFactor);
currentRawReading=atan2(accelerationY, accelerationX);
label.rotation=-RadiansToDegrees(currentRawReading);
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
#end

Related

how to do smooth movement of sprites in cocos2d

In my cocos2d project I am moving sprites from left most corner to the right most corner using CCMoveBy action. here is the code
CCSprite* sprite1 = [CCSprite spriteWithFile:#"Icon.png"];
sprite1.position = ccp(100, 100);
[self addChild:sprite1];
[sprite1 runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:4 position:ccp(300, 0)],
[CCMoveBy actionWithDuration:2 position:ccp(0, 200)],
[CCMoveBy actionWithDuration:4 position:ccp(-300, 0)],
[CCMoveBy actionWithDuration:2 position:ccp(0, -200)], nil]];
the sprite is not moving smoothly, instead it stuck sometimes while moving.
someone asked a similar question In cocos2d forum
http://www.cocos2d-iphone.org/forums/topic/ccactions-not-running-smoothly-specifically-ccmoveby-and-ccrotateby/
but In my game I am using action sequences at so many places and it would be too much to code every movement sequence by scheduling update or custom selectors.
i think you can achieve this by following code
MovingSprite.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface MovingSprite : CCSprite {
float _vx;
float _vy;
}
-(void) update:(ccTime)dt;
#property (nonatomic, assign) float vx;
#property (nonatomic, assign) float vy;
#end
MovingSprite.m
#import "MovingSprite.h"
#implementation MovingSprite
#synthesize vx = _vx;
#synthesize vy = _vy;
-(void)update:(ccTime)dT
{
self.vy -= (kGravity * dT); //optionally apply gravity
self.position = ccp(self.position.x + (self.vx*dT), self.position.y + (self.vy*dT));
}
And add [self scheduleUpdate]; to the init method of your game layer. Then add an update method within the game layer where you call update for all moving sprites.

Trying to implement radial gravity with Cocos2d 2.X

I am trying to learn how to implement radial gravity to specific objects and I was using the tutorial on (http://www.vellios.com/2010/06/11/radial-gravity-w-box2d-source/), but this tutorial is written an older version of Cocos2d. I have tried to fixed it as much as I could. If someone can point me in the right direction, i would really appreciate. Here is what I have so far. I commented the places that I am having problems with. Thank you.
HelloWorldLayer.h
#import <GameKit/GameKit.h>
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#define PTM_RATIO 32
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
b2World* world; // strong ref
GLESDebugDraw *m_debugDraw; // strong ref
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
HelloWorldLayer.mm
// Import the interfaces
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#define PTM_RATIO 32
#define kRADIAL_GRAVITY_FORCE 250.0f
enum {
kTagParentNode = 1,
kTagTileMap = 1,
kTagSpriteSheet = 1,
kTagAnimation1 = 1,
};
#pragma mark - HelloWorldLayer
#interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
-(void) createMenu;
#end
#implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if( (self=[super init])) {
// enable events
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize screenSize = [CCDirector sharedDirector].winSize;
CCLOG(#"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
// This will speed up the physics simulation
bool doSleep = true;
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
//Set up sprite
CCSprite *sheet = [CCSprite spriteWithFile:#"blocks.png"];
[self addChild:sheet z:0 tag:kTagSpriteSheet];
[self addNewSpriteAtPosition:ccp(screenSize.width/2, screenSize.height/2)];
//ERROR Use of undeclared identifier 'CCLabel'
//ERROR Use of undeclared identifier 'label'
CCLabel *label = [CCLabel labelWithString:#"Tap Screen" fontName:#"Marker Felt" fontSize:32];
// ERROR Use of undeclared identifier 'label'
[self addChild:label z:0];
//ERROR Use of undeclared identifier 'label'
[label setColor:ccc3(0,0,255)];
//ERROR Use of undeclared identifier 'label'
label.position = ccp( screenSize.width/2, screenSize.height-50);
// Create our static "Planet" - Nick
b2CircleShape shape;
shape.m_radius = 1.0f;
shape.m_p.Set(8.0f, 8.0f);
b2FixtureDef fd;
fd.shape = &shape;
//ERROR Use of undeclared identifier 'planet'
planet = groundBody->CreateFixture(&fd);
// End Create Planet - Nick
[self schedule: #selector(tick:)];
// init physics
[self initPhysics];
[self scheduleUpdate];
}
return self;
}
-(void) dealloc
{
delete world;
world = NULL;
delete m_debugDraw;
m_debugDraw = NULL;
[super dealloc];
}
-(void) createMenu
{
}
-(void) initPhysics
{
CGSize s = [[CCDirector sharedDirector] winSize];
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);
// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);
// Debug Draw functions
m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
// flags += b2Draw::e_jointBit;
// flags += b2Draw::e_aabbBit;
// flags += b2Draw::e_pairBit;
// flags += b2Draw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2EdgeShape groundBox;
// bottom
groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// top
groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
// left
groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox,0);
// right
groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
}
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_VERTEX_ARRAY,
// Unneeded states: GL_TEXTURE_2D, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
glDisable(GL_TEXTURE_2D);
glDisable(GL_COLOR_BUFFER_BIT);
world->DrawDebugData();
}
-(void) addNewSpriteAtPosition:(CGPoint)p
{
CCLOG(#"Add sprite %0.2f x %02.f",p.x,p.y);
CCSprite *sheet = (CCSprite*) [self getChildByTag:kTagSpriteSheet];
int idx = (CCRANDOM_0_1() > .5 ? 0:1);
int idy = (CCRANDOM_0_1() > .5 ? 0:1);
//ERROR Cannot initialize a parameter of type 'CCTexture2D *' with an lvalue of type 'CCSprite *'
CCSprite *sprite = [CCSprite spriteWithTexture:sheet rect:CGRectMake(32 * idx,32 * idy,32,32)];
[sheet addChild:sprite];
sprite.position = ccp( p.x, p.y);
// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
}
-(void) update: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
b2Body* ground = planet->GetBody();
b2CircleShape* circle = (b2CircleShape*)planet->GetShape();
// Get position of our "Planet" - Nick
b2Vec2 center = ground->GetWorldPoint(circle->m_p);
// Get position of our current body in the iteration - Nick
b2Vec2 position = b->GetPosition();
// Get the distance between the two objects. - Nick
b2Vec2 d = center - position;
// The further away the objects are, the weaker the gravitational force is - Nick
float force = kRADIAL_GRAVITY_FORCE / d.LengthSquared(); // 150 can be changed to adjust the amount of force - Nick
d.Normalize();
b2Vec2 F = force * d;
// Finally apply a force on the body in the direction of the "Planet" - Nick
b->ApplyForce(F, position);
if (b->GetUserData() != NULL) {
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self addNewSpriteAtPosition:location];
}
}
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
static float prevX=0, prevY=0;
//#define kFilterFactor 0.05f
#define kFilterFactor 1.0f // don't use filter. the code is here just as an example
float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
prevX = accelX;
prevY = accelY;
// accelerometer values are in "Portrait" mode. Change them to Landscape left
// multiply the gravity by 10
b2Vec2 gravity( -accelY * 10, accelX * 10);
world->SetGravity( gravity );
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end

box2d accelerometer how to make it work

I'm trying to make the box2d accelerometer work, I have a car sprite and want it to move left and right, when the iPhone is tilted.
Here is the code for the sprite:
- (void)spawnCar {
car = [CCSprite spriteWithSpriteFrameName:#"car.jpg"];
car.position = ccp(160, 250);
car.tag = 2;
[self addBoxBodyForSprite:car];
[_spriteSheet addChild:car];
}
How can implement the accelerometer to work for left and right?
Just do this...
in your init add
self.isAccelerometerEnabled = YES;
and then add this method...
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
b2Vec2 gravity(-acceleration.y * 15, acceleration.x *15);
world->SetGravity(gravity);
}

Dragging a specific UIImageView

In my code I have an array of objects which are UIImageViews with UIImages in them.
I use a for loop in -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event to move all objects from the array one by one.
Once they are moved to different positions on the main view I would like to be able to grab any of those objects and move them somewhere else.
I'm not sure how best to do this. Is there a way I can use touchesbegin so that I can drag only the item I clicked on?
Basically, there are five UIImageViews there now and each one has UIImage and I am trying to get to the point where when i click on any of them they are able to move around.
EDIT: John Muchow posted this on a blog here. You should be able to adapt for your own purposes.
//UIDraggableImageView.h
#import <UIKit/UIKit.h>
#interface UIDraggableImageView : UIImageView
{
CGPoint currentPoint;
}
#end
//UIDraggableImageView.m
#import "UIDraggableImageView.h"
#implementation UIDraggableImageView
- (id)initWithImage:(UIImage *)image
{
if (self = [super initWithImage:image]) {
self.userInteractionEnabled = YES;
}
return self;
}
-(void)dealloc {
[super dealloc];
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
// When a touch starts, get the current location in the view
currentPoint = [[touches anyObject] locationInView:self];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
// Get active location upon move
CGPoint activePoint = [[touches anyObject] locationInView:self];
// Determine new point based on where the touch is now located
CGPoint newPoint = CGPointMake(self.center.x + (activePoint.x - currentPoint.x),
self.center.y + (activePoint.y - currentPoint.y));
//--------------------------------------------------------
// Make sure we stay within the bounds of the parent view
//--------------------------------------------------------
float midPointX = CGRectGetMidX(self.bounds);
// If too far right...
if (newPoint.x > self.superview.bounds.size.width - midPointX)
newPoint.x = self.superview.bounds.size.width - midPointX;
else if (newPoint.x < midPointX) // If too far left...
newPoint.x = midPointX;
float midPointY = CGRectGetMidY(self.bounds);
// If too far down...
if (newPoint.y > self.superview.bounds.size.height - midPointY)
newPoint.y = self.superview.bounds.size.height - midPointY;
else if (newPoint.y < midPointY) // If too far up...
newPoint.y = midPointY;
// Set new center location
self.center = newPoint;
}
#end
I've done something similar with great success using the UIPanGestureRecognizer in my draggable view. To hook it up, it might look something like this:
#implementation DraggableView
-(id)initWithFrame:(CGRect)frame
{
...
...
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)] autorelease];
[self addGestureRecognizer:panGesture];
..
return self;
}
#end
In your pan: method, you could check the state of the gesture and even capture the gesture's location and use it to relocate your view. Here's a rough example of what it might look like:
-(void)pan:(UIPanGestureRecognizer *)gesture
{
if (gesture.state==UIGestureRecognizerStateChanged)
{
//We're moving!
UIView *aRootView = <perhaps your root view controller's view>;
CGPoint currentOrigin = [gesture translationInView:aRootView];
self.frame = CGRectMake(currentOrigin.x,currentOrigin.y,self.frame.size.width,self.frame.size.hight);
...
...
}
}
Here is more reading on the UIPanGestureRecognizer.
You might want to consider adding gesture recognizers to the views that you want to move, that way you can easily tell when they are tapped and you can use touchesMoved to move them around...
On a button click add an image from camera roll to the imageview. Image can zoom in and out and should drag that image to any place in the view.

iOS Core Animation: CALayer bringSublayerToFront?

How do I bring a CALayer sublayer to the front of all sublayers, analogous to -[UIView bringSubviewToFront]?
I'm curious why none of these answers mention the zPosition attribute on CALayer. Core Animation looks at this attribute to figure out layer rendering order. The higher the value, the closer it is to the front. These answers all work as long as your zPosition is 0, but to easily bring a layer to the front, set its zPosition value higher than all other sublayers.
This is variation of #MattDiPasquale's implementation which reflects UIView's logic more precisely:
- (void) bringSublayerToFront:(CALayer *)layer
{
[layer removeFromSuperlayer];
[self insertSublayer:layer atIndex:[self.sublayers count]];
}
- (void) sendSublayerToBack:(CALayer *)layer
{
[layer removeFromSuperlayer];
[self insertSublayer:layer atIndex:0];
}
Note: if you don't use ARC, you may wish to add [layer retain] at top and [layer release] at bottom of both functions to make sure layer is not accidentally destructed in a case it has retain count = 1.
Right code here
- (void)bringSublayerToFront:(CALayer *)layer {
CALayer *superlayer = layer.superlayer;
[layer removeFromSuperlayer];
[superlayer insertSublayer:layer atIndex:[superlayer.sublayers count]];
}
- (void)sendSublayerToBack:(CALayer *)layer {
CALayer *superlayer = layer.superlayer;
[layer removeFromSuperlayer];
[superlayer insertSublayer:layer atIndex:0];
}
Swift 4 version.
Idea that layer itself has bringToFront and sendToBack methods.
#if os(iOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif
extension CALayer {
func bringToFront() {
guard let sLayer = superlayer else {
return
}
removeFromSuperlayer()
sLayer.insertSublayer(self, at: UInt32(sLayer.sublayers?.count ?? 0))
}
func sendToBack() {
guard let sLayer = superlayer else {
return
}
removeFromSuperlayer()
sLayer.insertSublayer(self, at: 0)
}
}
Usage:
let view = NSView(frame: ...)
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.gray.cgColor
let l1 = CALayer(...)
let l2 = CALayer(...)
view.layer?.addSublayer(l1)
view.layer?.addSublayer(l2)
l1.bringToFront()
Create a category of CALayer like this:
#interface CALayer (Utils)
- (void)bringSublayerToFront;
#end
#implementation CALayer (Utils)
- (void)bringSublayerToFront {
CGFloat maxZPosition = 0; // The higher the value, the closer it is to the front. By default is 0.
for (CALayer *layer in self.superlayer.sublayers) {
maxZPosition = (layer.zPosition > maxZPosition) ? layer.zPosition : maxZPosition;
}
self.zPosition = maxZPosition + 1;
}
#end
This is the correct code:
-(void)bringSubLayerToFront:(CALayer*)layer
{
[layer.superLayer addSubLayer:layer];
}
-(void)sendSubLayerToBack:(CALayer*)layer
{
[layer.superlayer insertSublayer:layer atIndex:0];
}
You can implement this functionality in a category on CALayer like so:
CALayer+Extension.h
#import <QuartzCore/QuartzCore.h>
typedef void (^ActionsBlock)(void);
#interface CALayer (Extension)
+ (void)performWithoutAnimation:(ActionsBlock)actionsWithoutAnimation;
- (void)bringSublayerToFront:(CALayer *)layer;
#end
CALayer+Extension.m
#import "CALayer+Extension.h"
#implementation CALayer (Extension)
+ (void)performWithoutAnimation:(ActionsBlock)actionsWithoutAnimation
{
if (actionsWithoutAnimation)
{
// Wrap actions in a transaction block to avoid implicit animations.
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
actionsWithoutAnimation();
[CATransaction commit];
}
}
- (void)bringSublayerToFront:(CALayer *)layer
{
// Bring to front only if already in this layer's hierarchy.
if ([layer superlayer] == self)
{
[CALayer performWithoutAnimation:^{
// Add 'layer' to the end of the receiver's sublayers array.
// If 'layer' already has a superlayer, it will be removed before being added.
[self addSublayer:layer];
}];
}
}
#end
And for easy access you can #import "CALayer+Extension.h" in your project's Prefix.pch (precompiled header) file.
I don't think such methods exist, but it's easy to roll your own.
// CALayer+Additions.h
#interface CALayer (Additions)
- (void)bringSublayerToFront:(CALayer *)layer;
- (void)sendSublayerToBack:(CALayer *)layer;
#end
// CALayer+Additions.m
#implementation CALayer (Additions)
- (void)bringSublayerToFront:(CALayer *)layer {
CALayer *superlayer = self.superlayer;
[self removeFromSuperlayer];
[superlayer insertSublayer:gradientLayer atIndex:[self.sublayers count]-1];
}
- (void)sendSublayerToBack:(CALayer *)layer {
CALayer *superlayer = self.superlayer;
[self removeFromSuperlayer];
[superlayer insertSublayer:gradientLayer atIndex:0];
}